面向对象特殊方法

跟运算符无关的特殊方法

类别 方法名
字符串/字节序列表示形式 __repr__,__str__,__ format__,__bytes__
  • __call__
  • __len__
  • __repr__, __str__
  • __getitem__, __setitem__, __delitem__
  • __dict__, dir()
  • __slots__
  • __iter__
  • __getattr__, __getattribute__
  • __mro__, super()

__call__

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class SpeciaMembers:
    # 类的构造方法
    def __init__(self):
        print("My name is yang")
    # 对象的构造方法
    def __call__(self):
        print("My age is 18")

# 创建一个对象,并且执行类的构造方法
obj = SpeciaMembers()

# 执行对象的构造方法
obj()

# 先执行类的构造方法,然后再执行对象的构造方法
SpeciaMembers()()

输出

➜  python_test python3 024-exercise-1.py
My name is yang
My age is 18
My name is yang
My age is 18
实例2
class Dog(object):
    def __init__(self,name):
        self.name = name
        self.__food = "骨头"

    def __call__(self, *args, **kwargs):
        print("running call ",args,kwargs)

d = Dog("xxx")()

执行结果

running call  () {}

修改成

d = Dog("xxx")
d(1,2,3,name="xxx")

执行结果则为

running call  (1, 2, 3) {'name': 'xxx'}

__len__

当要使用内建函数len(),而参数是DictDemo的实例的时候,那一定要实现类型中的__len__()方法

__repr__,__str__

  • __repr__
  • __str__
# 字符串表示形式, 把一个对象用字符串的形式表达出来以便辨认
# repr 通过 __repr__ 这个特殊方法来得到一个对象的字符串表示形式
# 如果没有实现 __repr__ , 我们在控制台打印一个向量的实例时, 得到的可能就是地址

# __repr__ 和 __str__ 的区别在于, 后者是在str()调用的时候被使用, 或是在用print函数打印一个对象的时候才被调用
# 如果你只想实现两个特殊方法中的一个, __repr__ 会是更好的选择, 因为如果一个对象没有 __str__ 函数, 而Python需要调用它的时候, 解释器会用 __repr__ 代替
class Dog(object):
    def __init__(self,name):
        self.name = name
        self.__food = "骨头"

    def __call__(self, *args, **kwargs):
        print("running call ",args,kwargs)

    def __str__(self):
        return "<obj: %s>" % self.name


d = Dog("xxx")
print(d)  # __str__

__getitem__,__setitem__,__delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据

  • __getitem__
  • __setitem__
  • __delitem__
#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class SpecialMembers:
    # 当执行obj['value']的时候就会自动执行 __getitem__ 方法, 并且把对象括号内的值当做__getitem__的值
    def __getitem__(self,item):
        print(item)

    def __setitem__(self,key,value):
        print(key,value)

    def __delitem__(self,key):
        print(key)
# 创建一个对象
obj = SpecialMembers()
# 自动执行__getitem__方法
obj['value']
# 自动执行__setitem__方法
obj['k1'] = "values"
# 自动执行__delitem__方法
del obj['key']

输出

➜  python_test python3 024-exercise-2.py
value
k1 values
key

特殊的

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class SpecialMembers:
    # 当执行obj['value']的时候就会自动执行__getitem__方法,并且把对象括号内的值当做__getitem__的值
    def __getitem__(self,item):
        print(item,type(item),"__getitem__")

    def __setitem__(self,key,value):
        print(key,value)

    def __delitem__(self,key):
        print(key)

obj = SpecialMembers()

obj[1:3]  # __getslice__/__getitem__

obj[1:3] = [11,22,33]  # __setslice__/__setitem__

del obj[1:3]  # __delslice__/__delitem__

输出

➜  python_test python3 024-exercise-3.py
slice(1, 3, None) <class 'slice'> __getitem__
slice(1, 3, None) [11, 22, 33]
slice(1, 3, None)
实例2
class Foo(object):
    def __init__(self):
        self.data = {}

    def __getitem__(self, item):
        print('__getitem__',item)
        return self.data.get(item)

    def __setitem__(self, key, value):
        print('__setitem__',key,value)
        self.data[key] = value

    def __delitem__(self, key):
        print('__delitem__',key)

f = Foo()
res = f['k1']
f['k2'] = 'xxx'
del f['k1']

print(type(f)) # <class '__main__.Foo'>

print(type(Foo)) # <class 'type'> 类是由type类实例化产生的

__dict__,dir()

  • __dict__
  • dir()

__dict__ 是一个字典, 用来存储对象属性,其键为属性名,值为属性的值。

dir()是一个函数, 返回的是一个list

dir()函数会自动寻找一个对象的所有属性,包括__dict__中的属性。

__dict__dir()的子集,dir()包含__dict__中的属性。

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class SpeciaMembers:
    """
    类的注释
    """
    def __init__(self):
        self.Name = "yang"
        self.Blog = "http://yjj.top"

# 获取类中的成员
print(SpeciaMembers.__dict__)
# 创建一个对象
obj = SpeciaMembers()
# 获取对象中的成员
print(obj.__dict__)

执行结果

{'__doc__': '\n    类的注释\n    ', '__dict__': <attribute '__dict__' of 'SpeciaMembers' objects>, '__weakref__': <attribute '__weakref__' of 'SpeciaMembers' objects>, '__module__': '__main__', '__init__': <function SpeciaMembers.__init__ at 0x1024029d8>}
{'Name': 'yang', 'Blog': 'http://yjj.top'}
实例2
class Dog(object):
    def __init__(self,name):
        self.name = name
        self.__food = "骨头"

    def __call__(self, *args, **kwargs):
        print("running call ",args,kwargs)



print(Dog.__dict__) # 类调用,打印类里面的所有属性,不包括实例属性
d = Dog("xxx")
print(d.__dict__) # 实例调用,打印所有实例属性,不包括类属性

__slots__

如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加nameage属性。

为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

__iter__

一个对象如果可以被for循环迭代时,说明对象中有__iter__方法, 且方法中有yield

#!/usr/bin/env python
# _*_ coding:utf-8 _*_

class SpecialMembers:
    def __iter__(self):
        yield 1
        yield 2
        yield 3
# 创建一个对象
obj = SpecialMembers()
# 如果执行for循环对象时,自动执行对象的__iter__方法,此时的__iter__就是一个生成器
for i in obj:
    print(i)

输出

➜  python_test python3 024-exercise-5.py
1
2
3

MRO列表, super()

我们定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表:

>>> C.mro()   # or C.__mro__ or C().__class__.mro()
[__main__.C, __main__.A, __main__.B, __main__.Base, object]

MRO列表顺序是通过C3 线性化算法来实现的, 一个类的MRO列表就是合并所有父类的MRO列表, 并遵循以下规则

  • 子类永远在父类前面
  • 如果有多个父类, 会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择, 选择第一个父类

super原理

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

cls代表类, inst代表实例, 上面代码做了两件事

  1. 获取inst的MRO列表
  2. 查找cls在当前MRO列表中的index, 并返回它的下一个类, 即 mro[index + 1]

当我们使用super(cls, inst)时, Python会在inst的MRO列表上搜索cls的下一个类