本文共 13594 字,大约阅读时间需要 45 分钟。
本编研究下python的内置属性/魔法方法/魔法函数
这里说的内置属性,是指
__xxx__
的方法和属性,有的地方也称为魔法方法,叫法不一样。
本文概要
__init__
和__new__
的顺序、使用?__getattribute__
干嘛的?__call__
作用,与callable
内置函数有着怎样的关系呢?__lt__
、__gt__
等__getattr__
、__setattr__
、__delattr__
何时触发?__add__
与__iadd__
区别,何时触发(+
、+=
)?什么时候会改变自身,什么时候会新建对象?__getitem__
、__setitem__
、__delitem__
何时触发?__iter__
、__next__
与for-in
循环的配合__mul__
、__rmul__
、__imul__
何时触发?什么时候会改变自身,什么时候会新建对象?如果上面几点问题都能回答上,那么可以跳过本篇文章了。本篇相关文章共三连弹。(端午节在家整理,有帮助记得点个👍,就是对我最大的肯定😘😘😘)
技巧:文章内容太多如何阅读?
浩瀚的网络中,你我的相遇也是种缘分,看你天资聪慧、骨骼精奇,就送你一场大造化吧,能领悟多少就看你自己了。
常用专有属性 | 说明 | 触发方式 |
---|---|---|
__init__ | 构造初始化函数 | 创建实例后,赋值时使用,在__new__ 后 |
__new__ | 生成实例所需属性 | 创建实例时 |
__class__ | 实例所在的类 | 实例.__class__ |
__str__ | 实例字符串表示,可读性 | print(类实例),如没实现,使用repr结果 |
__repr__ | 实例字符串表示,准确性 | 类实例 回车 或者 print(repr(类实例)) |
__del__ | 析构 | del删除实例 |
__dict__ | 实例自定义属性 | vars(实例.__dict__) |
__doc__ | 类文档,子类不继承 | help(类或实例) |
__getattribute__ | 属性访问拦截器 | 访问实例属性时 |
__delattr__(s,name) | 删除name属性 | 调用时 |
__gt__(self,other) | 判断self对象是否大于other对 | 调用时 |
__setattr__(s,name,value) | 设置name属性 | 调用时 |
__gt__(self,other) | 判断self对象是否大于other对象 | 调用时 |
__lt__(slef,other) | 判断self对象是否小于other对象 | 调用时 |
__ge__(slef,other) | 判断self对象是否大于或者等于other对象 | 调用时 |
__le__(slef,other) | 判断self对象是否小于或者等于other对象 | 调用时 |
__eq__(slef,other) | 判断self对象是否等于other对象 | 调用时 |
__call__(self,\*args) | 把实例对象作为函数调用 | 调用时 |
下面分别以object和list的内置属性(魔法方法)来讲解下,其他的有需要的可以留言补充。
使用
dir()
函数来可以打印出有哪些属性,下面会挑一些常用的演示
演示
class People: name: strclass MyTestCase(unittest.TestCase): def test_attr(self): print(dir(People))
如需要源码,可以查看。(关键代码都贴在文章里,不用看也行)
初始化时,调用__init__
方法
演示
class People: name: str = 'iworkh' def __init__(self, name): print('call...__init__') self.name = nameclass MyTestCase(unittest.TestCase): def test(self): people = People('沐雨云楼') # 创建对象时,调用__init__ print(people.name)
__ new__ ()
在__ init__()
之前被调用,用于生成实例对象.
利用这个方法和类属性的特性可以实现设计模式中的单例模式.单例模式是指创建唯一对象吗,单例模式设计的类只能实例化一个对象.
演示
class People: name: str = 'iworkh' def __new__(cls, *args, **kwargs): print('call...__new__') obj = object.__new__(cls) return obj def __init__(self, name): print('call...__init__') self.name = nameclass MyTestCase(unittest.TestCase): def test(self): people = People('沐雨云楼') # 创建对象先调用__new__,初始化时调用__init__ print(people.name)
__ str__ ()
用于表示对象代表的含义,返回一个字符串.实现了__ str__ ()
方法.
__ str__ ()
的执行.这样就把对象和字符串关联起来,便于某些程序的实现,可以用这个字符串来表示某个类演示
class People: name: str = 'iworkh' age: int = 20 def __str__(self) -> str: print("call...__str__") return "{ {'name': {}, 'age': {}}}".format(self.name, self.age)class MyTestCase(unittest.TestCase): def test(self): people = People() print(people) # {'name': iworkh, 'age': 20} print(str(people)) # {'name': iworkh, 'age': 20}
类文档,子类不会继承
help(类或实例)会触发
演示
class People: """ 人 """ name: str = 'iworkh' age: int = 20 def say(self): """ say method """ print('say')class MyTestCase(unittest.TestCase): def test(self): people = People() print(People.__doc__) # 类: 人 print(people.__doc__) # 对象:人 print(People.say.__doc__) # 类:say method print(people.say.__doc__) # 对象:say method print(help(People.say)) # 类:say method
实例自定义属性,只有_init__
和通过对象.xxx
赋值的属性,才会显示
vars(实例)
会触发
演示
class People: name: str = 'iworkh' age: int = 20 admin: bool = True def __init__(self): self.admin = Falseclass MyTestCase(unittest.TestCase): def test(self): peo = People() print(peo.__dict__) # {'admin': False},只有init和塞值的才会显示 print(vars(peo)) # {'admin': False} # 塞值后 peo.name = '沐雨云楼' print(peo.__dict__) # 对象的__dict__ , {'admin': False, 'name': '沐雨云楼'} print(vars(peo)) # {'admin': False, 'name': '沐雨云楼'} print(People.__dict__) # 类的__dict__, 很多,还包括__和自己定义的
属性访问拦截器
演示
class People: name: str = 'iworkh' password = 'iworkh123' age: int = 20 def __getattribute__(self, item): print("call .... __getattribute__") if item == 'password': return '保密' else: return object.__getattribute__(self, item)class MyTestCase(unittest.TestCase): def test(self): peo = People() print(peo.name) # iworkh print(peo.password) # 保密
警告
不要在__getattribute__
方法中调用self.xxxx,会死循环,而应该使用object.__getattribute__(self, item)
__name__
返回类名
演示
class People: name: str = 'iworkh'class MyTestCase(unittest.TestCase): def test_name(self): print(People.__name__) # 类 People
属性操作,无非就塞值、取值、删除属性
__setattr__
设置属性,设置属性值时,触发这个方法 (对象.属性
, setattr
)__getattr__
取属性,当属性不存在时,才会触发这个方法(对象.属性
,getattr
)__delattr__
删除属性,删除属性值时,触发这个方法(delattr, del)演示
class People: name: str = 'iworkh' password = 'iworkh123' def __getattr__(self, item): print('call...__getattr__') return 'no attribute' def __delattr__(self, item): print('call...__delattr__') object.__delattr__(self, item) def __setattr__(self, key, value): print('call...__setattr__') object.__setattr__(self, key, value)class MyTestCase(unittest.TestCase): def test(self): peo = People() setattr(peo, 'name', '沐雨云楼') # 塞值 # 取值 print(peo.name) # 存在, 不会触发__getattr__ print(peo.sex) # 不存在,调用 __getattr__ print(getattr(peo, 'name')) # 存在, getattr方式 不会触发__getattr__ print(getattr(peo, 'sex')) # 不存在,getattr方式 调用 __getattr__ # 删除 peo.password = "test_password" # 塞值 del peo.password delattr(peo, 'name')
定义__call__
类实例对象可以像调用普通函数那样,以对象名()
的形式使用。
关于call的可以阅读下这篇文章:
演示
class People: name: str = 'iworkh' age: int = 20 def __call__(self, **kwargs): print('call...__call__') self.name = kwargs['name'] self.age = kwargs['age']class MyTestCase(unittest.TestCase): def test(self): peo1 = People() print(peo1.name, peo1.age, sep="......") # iworkh......20 peo1(name='沐雨云楼', age=22) # 通过对象()方式就能修改值 print(peo1.name, peo1.age, sep="......") # 沐雨云楼......22
思考:
如何取得类/对中所有属性或者方法?
dir
:属性和方法都拿到hasattr
: 属性和方法都拿到callable
: 属性为False, 函数和方法是True
这有实现好的例子
里面的
obj_dict_tool.py
类的to_dic
方法
还可以看下这篇文章,使用了__call__
方式
print
的类如果没有__str__
,那么就会调用__repr__
的
演示
class People: name: str = 'iworkh' age: int = 20 def __str__(self) -> str: print("call...__str__") return "str --> { {'name': {}, 'age': {}}}".format(self.name, self.age) def __repr__(self): print("call...__repr__") return "repr --> { {'name': {}, 'age': {}}}".format(self.name, self.age)class MyTestCase(unittest.TestCase): def test(self): people = People() print(people) print(str(people))
将
__str__
代码注释后再试下
对象与对象比较时,得需要比较的方法才能判断出大小,需要实现下面一些方法
__lt__
: 小于 <
__gt__
: 大于 >
__le__
: 小于等于 <=
__ge__
: 大于等于 >=
__eq__
: 等于 =
当只定义了一个小于,没有定义大于时。在使用时也可以使用大于,会根据小于结果取反的。
演示
class People: def __init__(self, name, age): self.name = name self.age = age def __le__(self, other): print('call ... le') return self.age <= other.ageclass MyTestCase(unittest.TestCase): def test(self): people_san = People('三哥', 30) people_three = People('张三', 30) people_four = People('李四', 40) people_five = People('王五', 50) print(people_three <= people_four) # True print(people_five >= people_three) # True print(people_san >= people_three) # True print(people_san <= people_three) # True print(people_three >= people_four) # False
我们再来看下list的一些属性
属性 | 说明 |
---|---|
__add__ | + ,返回一个新列表 |
__iadd__ | += ,原列表上操作 |
__contains__ | in ,定义当使用成员测试运算符(in 或 not in)时的行为 |
__delitem__ | 定义删除容器中指定元素的行为 |
__getitem__ | 定义获取容器中指定元素的行为 |
__setitem__ | 定义设置容器中指定元素的行为 |
__iter__ | for 定义当迭代容器中的元素的行为 |
__next__ | for 定义当迭代容器中的元素的行为 |
__len__ | 定义当被 len() 调用时的行为 |
__mul__ | 乘触发,返回新列表 |
__rmul__ | 乘触发,右操作符,返回新列表 |
__imul__ | 乘触发,改变自己 |
append | 在列表后面追加元素 |
extend | 在列表后面追加另一个列表的元素 |
insert | 指定位置插入元素 |
copy | 复制 |
count | list.count(data) 统计列表中出现data的次数 |
index | list.count(data) 列表中元素的索引 |
pop | list.pop(index) 弹出指定位置的元素 |
remove | list.remove(obj) 删除列表中指定元素 |
clear | 清空list |
reverse | 列表元素倒序输出 |
sort | list.sort(reverse=False) 列表元素升序 |
下面只演示模仿方法
使用
dir()
函数来可以打印出有哪些属性,下面会挑一些常用的演示
演示
class MyTestCase(unittest.TestCase): def test_attr(self): print(dir([1,2,3]))
下面就来演示几个常用的魔法方法,以Alphabet类为例子,里面有个data属性为list类型,来存字母。
如需要源码,可以查看。(关键代码都贴在文章里,不用看也行)
__add__
: 和+
操作符对应,对象与对象相加,返回一个新列
__iadd__
: 和+=
操作符对应,添加一个元素,在原列表上操作 演示
class Alphabet: data = [] def __init__(self, value) -> None: self.data = value def __add__(self, other): print('call...__add__') return Alphabet(self.data + other.data) def __iadd__(self, other): print("call...__iadd__") self.data += other return selfclass MyTestCase(unittest.TestCase): def test(self): list_data = ['a', 'c', 'd'] alphabet = Alphabet(list_data) print(alphabet.data) print("*" * 50) # + 与 __add__ alphabet_add = Alphabet(['m', 'n']) alphabet_sum = alphabet + alphabet_add # 返回一个新的对象 print(alphabet.data) # 原来的没有变 ['a', 'c', 'd'] print(alphabet_sum.data) # ['a', 'c', 'd', 'm', 'n'] print("*" * 50) # += 与 __iadd__ alphabet += 'x' print(alphabet.data) # 在原对象上操作 ['a', 'c', 'd', 'x']
__contains__
:是否包含
演示
class Alphabet: data = [] def __init__(self, value) -> None: self.data = value def __contains__(self, item): print("call...__contains__") return item in self.dataclass MyTestCase(unittest.TestCase): def test(self): list_data = ['a', 'c', 'd'] alphabet = Alphabet(list_data) print(alphabet.data) print('a' in alphabet) # True print('x' in alphabet) # False
__getitem__
:取得元素
__setitem__
:设置元素 __delitem__
:删除元素 演示
class Alphabet: data = [] def __init__(self, value) -> None: self.data = value def __getitem__(self, index): print('call...__getitem__') return self.data[index] def __setitem__(self, key, value): print('call...__setitem__') self.data[key] = value def __delitem__(self, key): print("call...__delitem__") delattr(self, key)class MyTestCase(unittest.TestCase): def test(self): list_data = ['a', 'c', 'd'] alphabet = Alphabet(list_data) print(alphabet.data) print("*" * 30) # ['a', 'c', 'd'] # 取值 print(alphabet[1]) # c 取值 会触发__getitem__ # 设置值 alphabet[1] = 'b' # 设值 会触发__setitem__ print(alphabet.data) # ['a', 'b', 'd'] # 删除 del alphabet['data'] # 删除 等价于 delattr(alphabet, 'data') 会触发__delitem__ print(alphabet.data) # []
__iter__
:迭代器,返回自己,然后for可以循环调用next方法
__next__
: 每一次for循环都调用该方法(必须存在) 演示
class Alphabet: data = [] index = -1 def __init__(self, value) -> None: self.data = value def __iter__(self): print("call...__iter__") return self def __next__(self): print("call...__next__") self.index += 1 if self.index >= len(self.data): raise StopIteration() else: return self.data[self.index]class MyTestCase(unittest.TestCase): def test(self): list_data = ['a', 'c', 'd'] alphabet = Alphabet(list_data) print(alphabet.data) # for循环 和 __iter__ 以及 __next__配合 for item in alphabet: print(item)
__mul__
: 左操作符相乘,返回新的list,原list不变
__rmul__
: 右操作符相乘,返回新的list,原list不变 __imul__
: 改变原list 演示
class Alphabet: data = [] def __init__(self, value) -> None: self.data = value def __mul__(self, other): print("call...__mul__") return Alphabet(self.data * other) def __rmul__(self, other): print("call...__rmul__") return Alphabet(other * self.data) def __imul__(self, other): print("call...__imul__") return self.data * otherclass MyTestCase(unittest.TestCase): def test(self): list_data = ['a', 'c', 'd'] alphabet = Alphabet(list_data) print(alphabet.data) # __mul__ alphabet_3 = alphabet * 3 print(alphabet.data) print(alphabet_3.data) # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd'] print("*" * 50) # __rmul__ alphabet_r3 = 3 * alphabet print(alphabet.data) print(alphabet_r3.data) # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd'] print("*" * 50) # __imul__ alphabet *= 3 # 改变了原对象 print(alphabet) # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd']
__len__
: 这个简单,但调用len(obj)
会触发
演示
class Alphabet: data = [] def __init__(self, value) -> None: self.data = value def __len__(self): return len(self.data)class MyTestCase(unittest.TestCase): def test(self): list_data = ['a', 'c', 'd'] alphabet = Alphabet(list_data) print(len(alphabet)) # 3
内置属性(魔法方法)还有很多,只要记得一些常用的即可。
思考
__init__
和__new__
的顺序、使用__getattribute__
干嘛的?__call__
作用,与callable
内置函数有着怎样的关系呢?__lt__
、__gt__
等__getattr__
、__setattr__
、__delattr__
何时触发?__add__
与__iadd__
区别,何时触发(+
、+=
)?什么时候会改变自身,什么时候会新建对象?__getitem__
、__setitem__
、__delitem__
何时触发?__iter__
、__next__
与for-in
循环的配合__mul__
、__rmul__
、__imul__
何时触发?什么时候会改变自身,什么时候会新建对象?如果上面几个问题能过回答上,那么恭喜您,本节内容的精髓您都掌握了。
到这内置函数和内置属性(魔法方法)都介绍完了,记得要配合 一起学习哦
能读到文章最后,首先得谢谢您对本文的肯定,你的肯定是对我们的最大鼓励。
你觉本文有帮助,那就点个👍
你有疑问,那就留下您的💬 怕把我弄丢了,那就把我⭐ 电脑不方便看,那就把发到你📲转载地址:http://qzhws.baihongyu.com/