guxh的python笔记八:特殊方法
1,类的特殊方法
新建一个类,本章内容中的特殊方法如果不创建类或新增方法,默认使用的就是下面的类:
class Foo: """this is Foo""" typecode = 'd' def __init__(self, x): self.x = x def run(self): return self.x f = Foo(1)
__doc__:类的描述信息
print(f.__doc__) # this is Foo
__module_:对象所属modul,如果是当前执行文件的即__main__,如果是import的,可以查看所属的module
print(f.__module__) # __main__
import pandas as pd df = pd.DataFrame() print(df.__module__) # pandas.core.frame
__class__:对象所属类,等效于type
print(f.__class__) # <class '__main__.Foo'> print(type(f)) # <class '__main__.Foo'>
__dir__:
函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;
for i in dir(): print(i) 输出为: Foo __annotations__ __builtins__ __cached__ __doc__ __file__ __loader__ __name__ __package__ __spec__ f
带参数时,返回参数的属性、方法列表。
for i in dir(Foo): print(i) 输出为: __class__ __delattr__ __dict__ __dir__ __doc__ __eq__ __format__ __ge__ __getattribute__ __gt__ __hash__ __init__ __init_subclass__ __le__ __lt__ __module__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __sizeof__ __str__ __subclasshook__ __weakref__ run typecode
类的实例比类多了实例属性:
print(set(dir(f)) - set(dir(Foo))) # {'x'} print(set(dir(Foo)) - set(dir(f))) # set()
__mro__:打印类的层次结构
>>> FileNotFoundError.__mro__ (<class 'FileNotFoundError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
__dict__:打印类/实例的属性
dict只能使用obj.__dict__,不能使用dict(obj),因为后者会被视为字典的初始化
打印所有实例的属性(不包括类属性):
f = Foo(1) print(f.__dict__) # {'x': 1}
打印所有类属性(不包括实例的属性):
for k, v in Foo.__dict__.items(): print(k, v) 输出为: __module__ : __main__ __doc__ : this is Foo typecode : d __init__ : <function Foo.__init__ at 0x000001AC45314620> __dict__ : <attribute '__dict__' of 'Foo' objects> __weakref__ : <attribute '__weakref__' of 'Foo' objects>
注意__dir__和__dict__的区别:前者显示类/实例所有的属性/方法(如果是实例显示的是类+实例的属性/方法),后者显示类/实例属性键值对(类和属性只会分别显示自己特有的)
__call__:让类的实例变为可调用
class Foo: def __call__(self, *args, **kwargs): return 1 print(Foo()()) # 1,第一次()是实例化,第二次()是调用
__get__、__set__、__delete__:描述符协议,属性的设置/取值/删除
__getattr__、__setattr__、__delattr__:动态属性的设值/取值/删除,详见类的属性
__getattribute__:获取已经存在的属性值,__getattr__是获取不存在的属性值的方法
__getitem__、__setitem__、__delitem__:[]运算符,分量的获取/设置/删除
class Foo: def __init__(self, components): self._components = sorted(components) def __setitem__(self, key, value): self._components[key] = value def __getitem__(self, item): return self._components[item] def __repr__(self): return '<Foo: {}>'.format(self._components)
访问与设置分量:
f = Foo([3, 5, 1, 9]) # <Foo: [1, 3, 5, 9]> print(f) print(f[1]) # 3 f[0] = 10 print(f) # <Foo: [10, 3, 5, 9]>
__enter__,__exit__:上下文管理器,先触发__enter__,再触发with代码块,最后触发__exit__
class Foo: def __enter__(self): return 'enter获取的内容' # 可以没有返回内容 def __exit__(self, exc_type, exc_val, exc_tb): print('exit时只需的内容') with Foo() as f: print(f) # f获取的是__enter__返回的结果 输出为: enter获取的内容 exit时只需的内容
__slot__:存储实例属性方式改为元组方式
缺省存储实例属性是字典方式,在__dict__中,改为元组方式可以节省空间,但是属性范围受到slot元组限制
class Foo: __slots__ = ('x', 'y')
__reversed__:反向迭代协议,让序列类型对象支持反向迭代,如果没有该协议,可以先把对象变成列表再用列表的反向迭代协议
class Foo: def __init__(self, components): self.components = [x*x for x in components] def __reversed__(self): return reversed(self.components) # 委托给self.components背后的生成器 f = Foo([1, 3, 5, 7, 9]) for i in reversed(f): print(i)
__format__:
_formats = {'ymd': '{d.year}-{d.month}-{d.day}', 'mdy': '{d.month}/{d.day}/{d.year}'} class Foo: def __init__(self, year, month, day): self.year = year self.month = month self.day = day def __format__(self, code): if code == '': code = 'ymd' fmt = _formats[code] return fmt.format(d=self) f = Foo(2019, 1, 25) print(format(f)) # 2019-1-25 print(format(f, 'mdy')) # 1/25/2019
__new__:可以实例化时跳过构造函数
class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day >>>d = Date.__new__(Date) >>>d <__main__.Date object at 0x10b353400> >>>d.year AttributeError: 'Date' object has no attribute 'year'
__metaclass__:待补充
__init__:略
__del__:略
__str__:略
__repr__:返回的内容最好符合obj = eval(repr(obj)),即返回内容可以直接用来实例化
没有定义__repr__或者__str__时,返回的是对象的内存地址:
class User: def __init__(self, user_id): self.user_id = user_id u = User(3) print(u) # <__main__.User object at 0x000002E85D7383C8>
使用猴子补丁定义__repr__,再打印u:
def repr(instance): return 'User({})'.format(instance.user_id) User.__repr__ = repr u = User(3) print(u) # User(3)
u的repr返回内容可以直接拿来实例化:
u1 = eval(repr(u)) print(u1) # User(3) print(u is u1) # False,u和u1不是同一个实例
__contain__:in测试首先调用的特殊方法,下面自建了一个类似字典的Dict类,但是in测试测的是key,而不是values:
class Dict: def __init__(self, **kwargs): self.map = kwargs def __contains__(self, item): if item in self.map.values(): return True d = Dict(a=1, b=2) print('a' in d) # False print(1 in d) # True
2,操作需要支持的特殊方法
序列:__len__,__getitem__
切片:__getitem__
迭代/元组拆包:1)__iter__;2)__getitem__(index从0开始)
可散列:__hash__,__eq__
in测试:1)__contains__;2)__iter__;3)__getitem__
描述符:__set__,__get__,__delete__ (可部分实现)
动态属性存取:__getattr__、__setattr__、__delattr__
[]分量存取:__getitem__、__setitem__、__delitem__
运算符+= :1)__iadd__,2)__add__
上下文管理器:__enter__,__exit__
反向迭代:__reversed__