Python 类 - zhongjiajie/zhongjiajie.github.com GitHub Wiki

Python-类

py2和py3的区别

class声明是否带上object

# 默认, Python 3
class A:
    pass

# Python 2
class A(object):
    pass

super

通过super可以调用父类的方法,避免了重复代码,方便维护,是类继承设计的其中一个目的.super的简单使用

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)    # 42
print(my_sub.b)    # chickenman
# a变量的赋值没有放在子类中,而是放在父类中完成

py2和py3中的区别: python3中super() -> same as super(__class__, self), python2中则要使用super(CurrentClass, self)

所以python3中可以这样使用here

class C(B):
    def method(self, arg):
        super().method(arg)    # This does the same thing as:
                               # super(C, self).method(arg)

super深入

Python: super 没那么简单

单继承

class A:
    def __init__(self):
        self.n = 2

    def add(self, m):
        print('self is {0} @A.add'.format(self))
        self.n += m

class B(A):
    def __init__(self):
        self.n = 3

    def add(self, m):
        print('self is {0} @B.add'.format(self))
        super().add(m)
        self.n += 3

如果运行

b = B()
b.add(2)
print(b.n)

得到的结果如下:

self is <__main__.B object at 0x106c49b38> @B.add
self is <__main__.B object at 0x106c49b38> @A.add
8

多继承

class C(A):
    def __init__(self):
        self.n = 4

    def add(self, m):
        print('self is {0} @C.add'.format(self))
        super().add(m)
        self.n += 4


class D(B, C):
    def __init__(self):
        self.n = 5

    def add(self, m):
        print('self is {0} @D.add'.format(self))
        super().add(m)
        self.n += 5

运行

d = D()
d.add(2)
print(d.n)

输出结果如下

self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19

因为此时的MRO(Method Resolution Order)列表为[D, B, C, A, object]

property

通常我们将有实际意义的属性通过函数的方式暴露给用户使用,而不是直接将对象本身传递给用户,这样做可以对参数进行检验,也使得参数更加安全不能被任意的使用者调用,如果使用函数,则对应的get/set方法就会变成Obj.set_attr(value)/Obj.set_attr().这样对调用者比较不友好,他们更希望的get/set方法是obj.attr=value/obj.attr(和直接调用属性的方式一样)

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作

>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score      # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

还可以通过@property设置只读属性,如下,birth是可读可写的对象,age是只读对象

class Student(object):

    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        self._birth = value

    @property
    def age(self):
        return 2014 - self._birth

FAQ

init时如果为None设为默认值

不要在类中__init__中使用可变对象作为默认值,如果要使用默认值,应该在__init__中默认值设为None,且在函数体中实现可变对象的赋值

class tmp:
    def __init__(self, a, b=None):
        self.a = a
        self.b = b or [] # b or []或选取第一个不为None的值

new init哪个先执行

  • __new__: 返回一个新的类的实例
  • __init__: 对类实例进行初始化

⚠️ **GitHub.com Fallback** ⚠️