本编研究下python的内置属性/魔法方法/魔法函数
这里说的内置属性,是指
__xxx__
的方法和属性,有的地方也称为魔法方法,叫法不一样。
本文概要
- 1.
__init__
和__new__
的顺序、使用? - 2.
__getattribute__
干嘛的? - 3.
__call__
作用,与callable
内置函数有着怎样的关系呢? - 4.对象如何才能比较?
__lt__
、__gt__
等 - 5.
__getattr__
、__setattr__
、__delattr__
何时触发? - 6.
__add__
与__iadd__
区别,何时触发(+
、+=
)?什么时候会改变自身,什么时候会新建对象? - 7.
__getitem__
、__setitem__
、__delitem__
何时触发? - 8.迭代
__iter__
、__next__
与for-in
循环的配合 - 9.
__mul__
、__rmul__
、__imul__
何时触发?什么时候会改变自身,什么时候会新建对象?
如果上面几点问题都能回答上,那么可以跳过本篇文章了。本篇相关文章共三连弹。(端午节在家整理,有帮助记得点个👍,就是对我最大的肯定😘😘😘)
技巧:文章内容太多如何阅读?
- 先看文章的目录(TOC),掌握文章整体脉络
- 然后走马观花式,通篇阅读。如果文章有用,再细细研究;文章没用的直接忽略,找下篇。
- 如果为了解决问题的,解决完了有时间再细读
- 如果学习的,收藏起来,边学习,边敲代码实践 (别copy代码,自己敲)
- 收藏完,下次用的时候,忘记如何使用了,到文章直接CTRL+F查找关键字
浩瀚的网络中,你我的相遇也是种缘分,看你天资聪慧、骨骼精奇,就送你一场大造化吧,能领悟多少就看你自己了。㊙传承之地🙇
1.常见魔法函数
常用专有属性 | 说明 | 触发方式 |
---|---|---|
__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的内置属性(魔法方法)来讲解下,其他的有需要的可以留言补充。
2.object
使用
dir()
函数来可以打印出有哪些属性,下面会挑一些常用的演示
演示
class People:
name: str
class MyTestCase(unittest.TestCase):
def test_attr(self):
print(dir(People))
如需要源码,可以查看。(关键代码都贴在文章里,不用看也行)
2-1.init
初始化时,调用__init__
方法
演示
class People:
name: str = 'iworkh'
def __init__(self, name):
print('call...__init__')
self.name = name
class MyTestCase(unittest.TestCase):
def test(self):
people = People('沐雨云楼') # 创建对象时,调用__init__
print(people.name)
2-2.new
__ 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 = name
class MyTestCase(unittest.TestCase):
def test(self):
people = People('沐雨云楼') # 创建对象先调用__new__,初始化时调用__init__
print(people.name)
2-3.str
__ str__ ()
用于表示对象代表的含义,返回一个字符串.实现了__ str__ ()
方法.
- 可以直接使用print语句输出对象,
- 可以通过函数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}
2-4.doc
类文档,子类不会继承
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
2-5.dict
实例自定义属性,只有_init__
和通过对象.xxx
赋值的属性,才会显示
vars(实例)
会触发
演示
class People:
name: str = 'iworkh'
age: int = 20
admin: bool = True
def __init__(self):
self.admin = False
class 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__, 很多,还包括__和自己定义的
2-6.getattribute
属性访问拦截器
演示
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)
2-7.name
__name__
返回类名
演示
class People:
name: str = 'iworkh'
class MyTestCase(unittest.TestCase):
def test_name(self):
print(People.__name__) # 类 People
2-8.属性操作
属性操作,无非就塞值、取值、删除属性
__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')
2-9.call
定义__call__
类实例对象可以像调用普通函数那样,以对象名()
的形式使用。
关于call的可以阅读下这篇文章: Python __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
这有实现好的例子 python对象与dict互转
里面的
obj_dict_tool.py
类的to_dic
方法
还可以看下这篇文章call方法,使用了__call__
方式
2-10.repr
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__
代码注释后再试下
2-11.比较
对象与对象比较时,得需要比较的方法才能判断出大小,需要实现下面一些方法
__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.age
class 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
3.list
我们再来看下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类型,来存字母。
如需要源码,可以查看。(关键代码都贴在文章里,不用看也行)
3-1.添加
__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 self
class 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']
3-2.包含
__contains__
:是否包含
演示
class Alphabet:
data = []
def __init__(self, value) -> None:
self.data = value
def __contains__(self, item):
print("call...__contains__")
return item in self.data
class 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
3-3.元素操作
__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) # []
3-4.迭代
__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)
3-5.重复
__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 * other
class 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']
3-6.len
__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
4.总结
内置属性(魔法方法)还有很多,只要记得一些常用的即可。
思考
- 1.
__init__
和__new__
的顺序、使用 - 2.
__getattribute__
干嘛的? - 3.
__call__
作用,与callable
内置函数有着怎样的关系呢? - 4.对象如何才能比较?
__lt__
、__gt__
等 - 5.
__getattr__
、__setattr__
、__delattr__
何时触发? - 6.
__add__
与__iadd__
区别,何时触发(+
、+=
)?什么时候会改变自身,什么时候会新建对象? - 7.
__getitem__
、__setitem__
、__delitem__
何时触发? - 8.迭代
__iter__
、__next__
与for-in
循环的配合 - 9.
__mul__
、__rmul__
、__imul__
何时触发?什么时候会改变自身,什么时候会新建对象?
如果上面几个问题能过回答上,那么恭喜您,本节内容的精髓您都掌握了。
到这内置函数和内置属性(魔法方法)都介绍完了,记得要配合 内置函数一起学习哦
5.推荐
能读到文章最后,首先得谢谢您对本文的肯定,你的肯定是对我们的最大鼓励。
你觉本文有帮助,那就点个👍
你有疑问,那就留下您的💬
怕把我弄丢了,那就把我⭐
电脑不方便看,那就把发到你📲