Redian新闻
>
Python的魔术方法小结

Python的魔术方法小结

公众号新闻

特殊属性

属性含义
__name__类、函数、方法的名字
__module__类定义所在的模块名
__class__对象或类所属的类
__bases__类的基类的元组
__doc__类、函数的文档字符串,如果没有定义则为None
__mro__类的mro,class.mro()返回的结果保存在此
__dict__类或者实例的属性,可写的字典
__dir__返回类或者对象的所有成员名称列表,dir()函数就是调用__dir__().如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中收集信息

魔术方法

分类

  • 创建、初始化与销毁

    • __init__ 和 __del__

  • hash

  • bool

  • 可视化

  • 运算符重载

  • 容器和大小

  • 可调用对象

  • 上下文管理

  • 反射

__hash__

hash()的返回值,返回一个整数。如果定义这个方法该类实例就是可hash的.

class A:
def __init__(self, name, age=18):
self.name = name

def __hash__(self):
return 1

def __repr__(self):
return self.name

print(hash(A('tom')))
print((A('tom'),A('tom')))
print([A('tom'),A('tom')])
print('~~~~~~~~~~~~~')
s = {A('tom'),A('tom')}
print(s)
print({tuple('t'),tuple('t')})
print({('tom',),('tom',)})
print({'tom','tom'})

上例说明一个问题:哈希值相等并不意味着元素相等。两个元素可以有相同的哈希值(哈希冲突)。上例中,如果想在set中剔除相同的key,需要加上__eq__魔术方法

class A:
def __init__(self,name,age=18):
self.name = name

def __hash__(self):
return 1

def __eq__(self,other):
return self.name == other.name

def __repr__(self):
return self.name

print({A('tom'),A('tom')})

__eq__

对应==操作符,判断两个对象是否相等,返回bool值

和hash魔术方法的比较:

  • __hash__只是返回hash值作为set的key,但是去重需要__eq__来判断两个对象是否相等

  • hash值相等只是hash冲突,不能说明两个对象相等

  • 因此,一般提供__hash__方法只是为了作为set或者dict的key

  • 不可hash对象isinstance(p1,collections.Hashable)一定是False

__bool__

内建函数bool(),对象放在逻辑表达式的位置,调用这个函数返回bool值,没有定义__bool__()就找__len__()返回长度,非0为真,如果__len__也没有定义,则所有实例都为真

可视化魔术方法

__repr__ 内建函数repr()对一个对象获取字符串表达,调用该方法返回字符串表达,如果__repr__也没有定义,就直接返回object的定义,显示内存地址信息

__str__

str()函数,内建函数format()、print()函数调用,需要返回对象的字符串表达。如果没有定义,就去调用__repr__方法。返回字符串表达,如果__repr__没有定义,就直接返回对象的内存地址信息

__bytes__

bytes()函数调用,返回一个对象的bytes表达,返回bytes对象

class A:
def __init__(self,name,age=18):
self.name = name
self.age = age

def __repr__(self):
return 'repr:{},{}'.format(self.name,self.age)

def __str__(self):
return 'str: {},{}'.format(self.name,self.age)

def __bytes__(self):
import json
return json.dumps(self.__dict__).encode()

print(A('tom'))
print([A('tom')]) # 调用了列表的__repr__
print([str(A('tom'))])

运算符重载

运算符特殊方法含义
<,<=,==,>,>=,!=__lt__,__le__,__eq__,__gt__,__ge__,__ne__比较运算符
+,-,*,/,%,/,**,divmod__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__算术运算符,移位、位运算也有对应的方法

@functools.total_ordering装饰器

实现了各种算术运算符的重载,可以简化代码,但是少用这个装饰器

容器相关方法

方法意义
__len__内建函数len(),返回对象的长度,如果把对象当容器类型看,就如同list或者dict。__boool__函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在即返回非0为真
__iter__迭代容器时调用,返回一个新的迭代器
__contains__in成员运算符,没有实现就调用__iter__方法遍历
__getitem__实现self[key]访问。序列对象,key接受整数为索引,或者切片。对于set和dict,key为hashable。key不存在则引发keyerror异常
__setitem__和__getitem__的访问类似,是设置值的方法
__missing__字典或其子类使用。getitem()调用时,key不存在执行该方法

可调用对象

python中一切皆对象,包括函数

调用函数时其实是调用对象的__call__方法。

上下文管理

当一个类同时实现了__enter__()和__exit__()方法,它就属于上下文管理的对象

__enter__进入此对象相关的上下文。如果存在该方法,with会把该方法的返回值作为绑定到as字句中的指定变量上
exit 退出与此对象相关的上下文方法,保证了发生异常时不会影响exit魔术方法的

class Point:
def __init__(self):
print('init')
def __enter__(self):
print('enter')
def __exit__(self,exc_type,exc_val,exc_tb):
print('exit')

with Point() as f:
print('do sth')

with … as …语法,实际上是把__enter__方法赋值给as后的变量。

参数

  • __enter__方法没有其他参数

  • __exit__方法有三个参数,如果上下文退出的时候没有异常,这三个参数都是None:

    • exc_type 异常类型

    • exc_value 异常值

    • traceback 异常的追踪信息

    • __exit__方法返回一个等效True的值,则压制异常,否则,继续抛出异常

class Point:
def __init__(self):
print('init')

def __enter__(self):
print('enter')
return
def __exit__(self,exc_type,exc_val,exc_tb):
print(exc_type)
print(exc_val)
print(exc_tb)
print('exit')
return "abc"

p=Point()
with p as f:
raise Exception('New Error')
print('do sth')
print('outer')

上下文应用场景

  1. 增强功能。在代码执行前后增强代码,类似装饰器的功能

  2. 资源管理。打开了资源需要关闭,例如文件对象,网络连接,数据库连接等等

  3. 权限验证。执行代码之前做权限的验证,在__enter__中处理

contextlib.contextmanager

这是一个装饰器,实现了上下文管理,他装饰一个函数,而不用像类一样实现__enter__和__exit__方法。

反射相关的函数和方法

反射内置函数

  • getattr(object,name[,default]) 通过name返回object的属性值。当属性不存在,将使用default返回,没有default抛出异常,name必须是字符串

  • setattr(object,name,value) object属性存在则覆盖,不存在则新增

  • hasattr(object,name) 判断对象是否有这个名字的属性,name必须为字符串

class Point:
def __init__(self,x,y):
self.x = x
self.y = y

def __str__(self):
return "Point({},{})".format(self.x, self.y)

def show(self):
print(self)

p1 = Point(4,5)
p2 = Point(10,10)
print(repr(p1),repr(p2),sep='\n')
print(p1.__dict__)
setattr(p1,'y',16)
setattr(p1,'z',10)
print(getattr(p1,'__dict__'))

# 动态增加方法
# 为类增加方法
if not hasattr(Point,'add'):
setattr(Point,'add',lambda self,other: Point(self.x + other.x, self.y + other.y))

print(Point.add)
print(p1.add)
print(p1.add(p2)) # 绑定


# 为示例增加方法,未绑定
if not hasattr(p1,'sub'):
setattr(p1,'sub',lambda self,other:Point(self.x - other.x, self.y - other.y))

print(p1.sub(p1,p1))
print(p1.sub)

# add在谁里面,sub在谁里面
print(p1.__dict__)
print(Point.__dict__)

反射相关的魔术方法

__getattr__()

class Base:
n = 0

class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y

def show(self):
print(self.x,self.y)

def __getattr__(self,item):
return "missing {}".format(item)

p1 = Point(4,5)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t) # missing

一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出异常表示找不到属性

setattr()

class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y

def show(self):
print(self.x,self.y)

def __getattr__(self,item):
return "missing {}".format(kry,value)

def __setattr__(self,key,value):
print("setattr {}={}".format(key,value))
self.__dict__[key] = value

可以拦截对示例属性的增加、修改操作,如果要设置生效,需要自己操作实例的__dict__

__getattribute__方法

class Base:
n = 0

class Point(Base):
z = 6
def __init__(self,x,y):
self.x = x
self.y = y

def __getattr__(self,item):
return "missing {}".format(item)

def __getattribute__(self,item):
return item

p1 = Point(4,5)
print(p1.__dict__)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.t)
print(Point.__dict__)
print(Point.z)

示例的所有属性访问,第一个都会调用__getattribute__,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出异常。它的return值将作为属性查找的结果。它的return值将作为属性查找的结果,如果抛出异常,则直接调用__getattr__方法,因为属性没有找到。

__getattribute__方法中为了避免在该方法中无限递归,它的实现中应该永远调用基类的同名方法访问需要的任何属性,除非你明确地知道__getattribute__方法用来做什么

链接:https://blog.csdn.net/syaziou/article/details/109081794

(版权归原作者所有,侵删)


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
阿瑟新剧大火!然而学Python的看了都在骂人如何在 Ubuntu 和其他相关 Linux 中安装 Python 3.10 | Linux 中国在 VS Code 和 Codium 中编写 Python 程序 | Linux 中国Arduino宣布支持MicroPythonPython 3.11 终于发布了,性能大提升!澳门仙境下午茶,还有疯帽子的魔术~犹太名人堂 | 史上最伟大的魔术师 Harry Houdini速领!清华大学出品《Python漫画教学书》!零基础3天就能速成"叔叔建议我远离 Python,从汇编语言学起......"加拿大制造业劳工短缺习家军的形成Julia 快到离谱?不,它并没有比Python快340000,000,000倍三姓家奴,一脸阴沉, 到时候下手最狠的恐怕就是他Potato salad为什么适用于Python的TensorFlow正在缓慢消亡ChatGPT发明「史莱姆语」,词汇语法规则全都有,还配了「史翻英」Python代码豪赚5W!居家做Python私活,真香!Python如何修改美女桌面60 个重要的 Python 小示例圣诞礼物 | 酷炫好玩的魔术礼盒来了,提升手眼协调能力,节日送娃好选择!3天速成!这份清华大学出品的《Python漫画书》领到就是赚到!“可在浏览器端运行的Python”再发力Python 3.11 正式版来了,比 3.10 快 10-60%Python 3.11 正式版发布,比 3.10 快 10-60%,官方:这或许是最好的版本两天赚1w,用Python接私活真香ChatGPT竟写出毁灭人类计划书,还给出相应Python代码,网友:AI正在指数级发展10 个杀手级的 Python 自动化脚本!狂揽两千星,速度百倍提升,高性能Python编译器Codon开源如何在 Ubuntu 等 Linux 中安装 Python 3.11 | Linux 中国R语言工作者入门python,你需要实际解决这三个问题阿瑟新剧大火!然而学Python的留学生看了都在骂人在加国金融圈,SQL比Python更靠谱秋游河溪-16里溪,狮子头公园速领!清华大学出品《Python教学书》!零基础3天就能速成
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。