欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python 类

程序员文章站 2022-07-15 17:06:59
...

用来描述具有相同的属性和方法的对象的集合。python中的类就是对象。

python 中的元类详解

创建Dog类,赋予dog蹲下(sit())和打滚(roll_over())

class Dog():
    def __init__(self,name,age):
        self.name=name;
        self.age=age;
    def sit(self):
        print(self.name.title()+" is now sitting.")
    def roll_over(self):
        print(self.name.title()+" rolled over!")

super()和init()和new()方法

首先super,我们都知道他的意思是继承父类的含义,但是python中继承父类的实现可以直接再括号中写例如ChildA和childB,这两个是一样的

class Base(object):
    def __init__(self):
        print "Base created"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super().__init__()

print ChildA(),ChildB()

但是使用super的好处,是避免了书写父类的名,主要用于多重继承,有降偶的好处

然后是init(),它类似与构造函数,在类创建对象的时候使用,用于初始化变量等操作

每当创建新实例时,Python都会自动运行它
init()中的形参self必不可少,还必须位于其他形参的前面。self不是代表类,而是代表的是实例。同时在继承时调用父类的init()方法。

new() 函数只能用于从object继承的新式类。

这里扯一嘴,什么是新式类与旧式类?
python2.1之前只存在旧式类

#新式类
class A(object):
    pass
#旧式类
class A:
    pass

但是在python3中只存在新式类。

  • 新式类的MRO(method resolution order 基类搜索顺序)算法采用C3算法广度优先搜索,而旧式类的MRO算法是采用深度优先搜索

  • 新式类相同父类只执行一次构造函数,经典类重复执行多次。

  • 旧式类中,类名和type是无关的

class Demo(object):
  def __init__(self):
    print('__init__() called...')
 
  def __new__(cls, *args, **kwargs):
    print('__new__() - {cls}'.format(cls=cls))
    return object.__new__(cls, *args, **kwargs)
 
if __name__ == '__main__':
  de = Demo()
  
__new__() - <class '__main__.Demo'>
__init__() called...

实例化对象的时候,调用init()初始化之前,先调用了new()方法,new()必须要有返回值,返回实例化出来的实例,可以直接将object的new()出来的实例返回。

init()有一个参数self,该self参数就是new()返回的实例,init()在new()的基础上可以完成一些其它初始化的动作,init()不需要返回值。

class A(object):
  def __init__(self):
    print('__init__() Acalled...')
 
  def __new__(cls, *args, **kwargs):
    print('__A_new__() - {cls}'.format(cls=cls))
    return object.__new__(cls, *args, **kwargs)

class Demo(A):
  def __init__(self):
    print('__init__() called...')
 
  def __new__(cls, *args, **kwargs):
    print('__new__() - {cls}'.format(cls=cls))
    return A.__new__(cls, *args, **kwargs)
 
if __name__ == '__main__':
  de = Demo()
  
__new__() - <class '__main__.Demo'>
__A_new__() - <class '__main__.Demo'>
__init__() called...

由上可知,可以return父类new()出来的实例,这时候的顺序是,子类new,父类new,子类init()

class A(object):
  def __init__(self):
    print('__init__() Acalled...')
 
  def __new__(cls, *args, **kwargs):
    print('__A_new__() - {cls}'.format(cls=cls))
    return object.__new__(cls, *args, **kwargs)

class Demo(A):
  def __init__(self):
    print('__init__() called...')
 
  def __new__(cls, *args, **kwargs):
    print('__new__() - {cls}'.format(cls=cls))
    
 
if __name__ == '__main__':
  de = Demo()

__new__() - <class '__main__.Demo'>

如果new()不返回,或返回错误,不会运行init(),但是如果不写new,则是默认实现了new,会运行init函数。

如果类比作制造商,new()方法就是前期的原材料购买环节,init()方法就是在有原材料的基础上,加工,初始化商品环节。

类变量和实例变量

类变量就是供类使用的变量,实例变量就是供实例使用的.
类变量和实例变量就类似与函数的传参问题
具体可参考

python中值传递还是引用传递?

class Person:
    name="aaa"
 
p1=Person()
p2=Person()
p1.name="bbb"
print(p1.name)  # bbb
print(p2.name)  # aaa
print(Person.name)  # aaa

p1.name="bbb"是实例变量,p1.name一开始是指向的类变量name="aaa",但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了.但是这里如果是一个list数组就不一样了。

Python内置类属性

  • dict : 类的属性(包含一个字典,由类的数据属性组成)
  • doc :类的文档字符串
  • name: 类名
  • module: 类定义所在的模块(类的全名是'main.className',如果类位于一个导入模块mymod中,那么className.module 等于 mymod)
  • bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)

Python中单下划线和双下划线

  • 首先foo: 定义的是特殊方法,一般是系统定义名字 ,类似 init() 之类的。

  • 其次_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *

  • 最后__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。

@staticmethod和@classmethod

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)
 
    @classmethod
    def class_foo(cls,x):
        print( "executing class_foo(%s,%s)"%(cls,x))
 
    @staticmethod
    def static_foo(x):
        print ("executing static_foo(%s)"%x)
 
a=A()

这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self, x),为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a, x)).类方法一样,只不过它传递的是类而不是实例,A.class_foo(x).注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好.
对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用a.static_foo(x)或者A.static_foo(x)来调用.

实例方法 类方法 静态方法
a = A() a.foo(x) a.class_foo(x) a.static_foo(x)
A 不可用 A.class_foo(x) A.static_foo(x)

在谈及重写与重载前,先复习下概念

  • 重载Overloading是一个类中多态性的一种表现。
    多个同名函数同时存在,具有不同的参数个数/类型。
  • 1 )、参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载。

2)、返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载。

3)、访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)

4)、重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。

python 重写

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Parent:        # 定义父类
   def myMethod(self):
      print '调用父类方法'
 
class Child(Parent): # 定义子类
   def myMethod(self):
      print '调用子类方法'
 
c = Child()          # 子类实例
c.myMethod()         # 子类调用重写方法

Python中函数重载??

python 不支持函数重载
函数重载主要是为了解决两个问题。

  • 1.可变参数类型。
  • 2.可变参数个数。

另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。