内置魔法方法参见:官网:Python数据模型
Python 魔术方法指南 ——稍微有点乱
关于Python的私有属性和魔法方法
魔法方法都是 _ xx _ 形式的,虽然 _ _开头,但是实例却可以直接访问,所以魔法方法,并不属于私有属性。当然自己也可以定义 _ xx 的魔法方法。
只有
举个简单例子 _ init _ 可以直接被继承,子类可以直接 使用,就肯定不是私有的了。哈哈哈
私有属性(包括私有方法),实例或继承都不可以直接访问,但是可以通过,实例名 或者 直接访问 _ classname. _私有属性名 访问(这也算是一个突破私有限制的通道)
其实可以调用的属性都可以用 dir(obj),查看
class confirm_obj_magic_func():
"类的说明,可被自动收集 这个说明会自动传递给魔法属性 _ _doc_ _,
当然你可以自定义这样就会覆盖掉默认的类描述信息"
def __init__(self,a):
self.__a = a
def __a__(self):
print("这是一个普通方法")
def _b(self):
print("这是单下划线普通的方法")
def __b_(self):
print("这个也是私有方法")
def __b___(self):
print("后面_ 超过两个(含两个),都不属于私有属性,无论前面是不是 _ _")
def __doc__(self):
return "这是一个魔法方法"
aa = confirm_obj_magic_func(1324)
aa.__a__()
这是一个普通方法
aa.__b_()
... object has no attribute '__b_'
aa.__b___()
后面_ 超过两个(含两个),都不属于私有属性,无论前面是不是 _ _
print(aa.__doc__())
这是一个魔法方法
如果删掉自定义的 __doc__(self)
print(aa.__doc__)
类的说明, 这个说明会自动传递给魔法属性 _ _doc_ _,
当然你可以自定义这样就会覆盖掉默认的类描述信息
print(__doc__()) # 这时候默认的 doc 只是属性,而不是方法属性
TypeError: 'str' object is not callable
python 如果是自定义的魔法(不是你自己单独定义的,默认是普通属性,不是 callable 的方法属性,不可 xx(),调用,自己定义的 相反必须 xx() 调用)
参考
刘江博客
-
_ str _ 默认返回 str(self) 当然也可自定义返回字符串
str() 返回的就是 object._ str _()
print() 默认也是返回 str(obj) 可以这样考虑,当然print 不止这简单
str 函数官方解释:
returns the result of object. _ str _() (if defined) or repr(object)
def __str__(self, *args, **kwargs): # real signature unknown
""" Return str(self). """
pass
如果你写pass ,就返回str(self) 这是解释器帮你调用一次,并返回
如果你自己写了 return str(self),那不好意思,会无线递归,因为str(self) 还
是会调用 self.__str__() 自己试了递归 330 次报错了
但是你返回其他字符串,就不会产生递归了,哈哈
def __repr__(self, *args, **kwargs): # real signature unknown
""" Return repr(self). """
pass
这边书写方式同 __str__
print(xxobj)
如果没有 __str__
返回类似的 实例内存地址
<__main__.confirm_obj_magic_func object at 0x0000000004E33780>
如果有 __str__
返回 str return 的内容,因为 print() 的定义就是先返回 对象的 __str__
如果你自己的 str 没有返回字符串,就会报错
如 当只返回数字:
return 12
__str__ returned non-string (type int)
返回其他格式就更不行了
print(xx) xx 参数会被转换为字符串,就像是执行了 str(xx)
一样
-
_ module _
表示当前操作的对象在属于哪个模块
print(aa.__module__)
__main__
-
_ class _ 获取当前对象所在的 属于的类对象
print(aa.__class__)
<class '__main__.confirm_obj_magic_func'>
# 打印出来是当前运行模块的某个类对象
有个疑问,为什么打印class名,没有出来 __str__ 的东西呢,
很好理解 __str__ 是实例魔法方法,类访问不到实例的方法
aa._ class _ 等价于一个类 对象
你都可以这样用
aa._ class _() 初始化和 aa 相同的实例了(因为和 aa 是同一个类初始化生成的)
-
_ del _() 析构方法,当对象在内存中被释放时,自动触发此方法。
此方法无需定义,因为python有垃圾回收,释放该对象内存的时会,自动调用这个方法
如果过你想在释放内存的时候,做点其他工作,那么你可以自定义 _ del _ 方法
如:
def __del__(self):
print("我被回收了!")
del xx实例
"我被回收了!"
-
_ call _()
如果为一个类编写了此方法,那么这个类实例化后可以加() 执行 _ call _
相当于是实例的直接运行的简单实现,其实和接着调用其他实例方法,一样的,只不过这个不用 . 而是直接加 () 来执行。主要优雅
class xx():
def __call__(self, *args, **kwargs):
print('__call__')
a = xx()
a() #这就调用了 _ _call_ _ 这个实例方法
问题来了,如何我们如果看代码,可以直接判断有 _ call _ 就是可以直接执行的,如果看不了代码,可以使用 Python 内置函数来判断
callable()
callable(max)
True
callable([13,])
false
-
_ dict _() 获取类或对象中所有成员
获取实例:
获取实例
-
对象字典操作三剑客
getitem()、setitem()、delitem()
定义了这三个方法之后,就可以愉快的使用 字典操作符, [ ] 了。
字典形式操作对象属性
-
_ len _() 获取对象长度
len(xx) 等价于 xx._ len _()
Python的 list、dict、str等内置数据类型都实现了该方法,但是你自定义的类要实现len方法需要好好设计
-
__ iter __() 迭代器方法:
列表、字典、元组之所以可以进行for循环,是因为其内部定义了 iter()这个方法。如果想使对象可迭代,那么就需要给他设置这个魔法函数。其实完全可以直接读取对象里面的东西,但是用魔法方法就比较简洁明了,有水平的存在。
该魔法方法需要返回一个克迭代的迭代器。(基本可迭代类型,列表,字典,字符串等等通过iter()转化即可,当然自己实现一个迭代器,那也是妥妥的)
用for循环遍历对象时,就会调用类的这个_ iter () ,我感觉是 iter 方法就是一个中转站(优美的连接当前对象到 iter 返回的克迭代的对象上面),把for 转移到了 iter 返回的可迭代的对象上面,类似于for i in xx. iter _ 就像 xx[ ] 调用 setitem() 一样。
但是可以用内置函数实现
iter 内置函数源码,可以看出 是把克迭代对象转化为 迭代器
关于可迭代,迭代器 ,生成器 参考
Python可迭代对象,迭代器,生成器的区别
def iter(source, sentinel=None): # known special case of iter
"""
iter(iterable) -> iterator
iter(callable, sentinel) -> iterator
Get an iterator from an object. In the first form, the argument must
supply its own iterator, or be a sequence.
In the second form, the callable is called until it returns the sentinel.
"""
pass
iter 返回迭代器
或者直接用生成器
生成器
-
_ _ next _ _()
参考 next 和 iter 的关系
https://www.programiz.com/python-programming/iterator
简单来说 如果用for 那就必须写 --iter--(因为for 内部首先调用 iter 函数) ,如果直接用 next,可以只写 --next--, 一般来说 -- iter-- 里面写一些 初始化,最后返回一个迭代器(如果有 next ,那一般返回 self)
上面也有个例子说,使用 --iter-- 里面直接返回 yield 或 iter(list),然后没有 --next-- 函数,这是因为 yield 和 iter内置函数会帮我们自动实现 ------next-- 函数
上面说的是对的,看两个例子对比下,就明白了
直接使用 next ,绕过 iter
class test():
def __init__(self,data=1):
self.data = data
def __next__(self):
if self.data > 5:
raise StopIteration
else:
self.data+=1
return self.data
i = test(3)
for item in range(3):
print(i.__next__())
4
5
6
class test():
def __init__(self,data=1):
self.data = data
#def __iter__(self):
# return self
def __next__(self):
if self.data > 5:
raise StopIteration
else:
self.data+=1
return self.data
for item in test(3):
print(item)
TypeError: 'test' object is not iterable
可以看出一旦,注释 --iter-- 直接for 这个对象,就会报错了
架上 --iter-- 就正常了。所以还需要了解 for 底层是先吊用了 --iter-- ,然后调用了 next。
绕过 --next--
class test():
def __init__(self,data=1):
self.data = data
def __iter__(self):
return iter([1,2,3])
def __next__(self):
if self.data > 5:
raise StopIteration
else:
self.data+=1
return self.data
for item in test(3): # 这里必须是运行状态,才是迭代器哦
注意此时,for 循环的迭代器,已经不是当前类实例了,而是 iter() 内置函数转化的 迭代器了。所以结果自然和当前实例的 --next-- 无关
其实你要是,直接使用 next(当前是实例),他还是返回实例里面的 --next--,和 --iter-- 无关,iter只是决定了 他的循环次数
print(item)
1
2
3
所以 --iter-- 如果直接返回了 迭代器,而不是 self ,那么已写的 --next-- 就只有直接使用 next() 函数时候,才有作用了
所以 理解 for 底层,十分重要哟。iter 和 next 函数确实默认调用到 当前迭代对象的 --iter-- 和 --next-- 。
-
repr()
和 _ str _ 类似,但是返回的是 堆栈的内存啥的,主要是给开发看的
如果你实现了 str ,他就直接返回 --str-- ,否则使用 --repr-- , 如果你自己没有 --repr-- 解释器返回默认的 对象地址格式
-
add: 加运算 sub: 减运算 mul: 乘运算 div: 除运算 mod: 求余运算 pow: 幂运算
略
-
author
这个魔法属性一般是在 文件里面的,也经常见到在文件最上面写上 _ auth _
author代表作者信息!类似的特殊成员还有很多,就不罗列了
-
_ slots _
这个也很熟悉了,限制实例的属性集合 ,类里面写的实例属性也会限制,外面添加更会限制的,哈哈哈,
子类不写不限制,子类写,会继承父类限制并加上自己的 slots 限制
-
赋值三剑客
参考:
_ setattr _, _ delattr _, _ getattr _ 到底干了什么
-- setattr -- --delattr -- --getattr-- 的使用场景
_ setattr _ 添加/修改属性会触发它的执行
_ getattr _只有在使用点调用属性且属性不存在的时候才会触发
class mm():
def __init__(self):
self.a = "正赋值"
print(self.b)
def __getattr__(self, key):
print("不存在的属性", key)
return 0
mm()
上面初始化 里面 使用 self.b 来调用 b 属性,但是 self 里面没有b ,所以此时触发 --getattr-- , 并且 使用 --getattr-- 的返回值
上面的结果:
不存在的属性 b
sgfsdgf
--delattr--
def __delattr__(self, item):
print('你在删除属性')
# del self.item #无限递归了,和上面的__setattr__原理一样 delattr(self, key) 也是一样的
self.__dict__.pop(item) 正确的做法
所以一般我们不会,设置这三个方法,因为正常的赋值取值 ,Python 基类里面都已经
帮我们弄好了。那这种情况,肯定是用在更高级的地方 前方高能:
使用 赋值三剑客实现 已经存在类型修改:
其实一种可以使用继承,来实现(因为继承,没有腹泻的方法属性,都会找到
父类里面的, 有些编程语言中如python2.2之前,基本数据类型不属于类,那样就不能被类继承,所以很多东西都实现不了,但是现在基本 py3 了,所以有限继承把),还有一种方法就是 _ getattr _ 实现授权(虽然可以通过继承,但这个也要知道的)
举个下的例子看看吧:
在原生List类的基础下,实现append方法只能插入int类型数据
执行新功能获取列表的中间index值
class List:
def __init__(self,onelist):
self.onelist=onelist #
#基于原生的List类,修改原生的append方法,只能插入int类型的数据
def append(self, p_object):
if not isinstance(p_object,int):
raise TypeError('must be int')
self.onelist.append(p_object)
@property
def getmid(self):
'新增自己的方法'
index = len(self.onelist) // 2
return self.onelist[index]
def __getattr__(self, item):
return getattr(self.onelist,item) #这是关键点,如果item在本类中不存在,
会去原生的List类里面取寻找item方法
l=List([1,2,3]) #等价于l=[1,2,3]
l.append(4)
print(l)
#l.append('abc') #报错,必须为int类型
#新增自己的方法,获取列表中间的index
print(l.getmid)
#基于授权,获得insert方法,insert方法在本类中没有实现,所以回去原生的List中寻找insert方法
#list.insert(index, obj)
l.insert(0,9)
print(l)
最后说明一下,魔法方法有的和内置函数有很紧密的关系,有可能是 魔法方法调用了默认函数,或者默认函数直接使用 魔法方法的返回值。所以内置函数还是很有必要掌握的。娃哈哈
pyhton 内置函数官方文档
注意python 很多内置方法都已经 帮我们定义好了返回值,很多都是 直接写了 pass
比如 上面讲的一个例子:
def __str__(self, *args, **kwargs): # real signature unknown
""" Return str(self). """
pass
如果你写pass ,就返回str(self) 这是解释器帮你调用一次,并返回
如果你自己写了 return str(self),那不好意思,会无线递归,因为str(self) 还
是会调用 self.__str__() 自己试了递归 330 次报错了
但是你返回其他字符串,就不会产生递归了,哈哈
还有很多类似的 比如:
class mm():
def __init__(self):
self.a = "正赋值"
def __setattr__(self, key, value):
print(key, value)
setattr(self, key, value) # self.key=value 也会递归,因为他会直接触发 --setattr-- (可以看出 setattr是先触发 . 操作,然后 . 就触发了 --setattr--)
正确的写法: self._ _dict_ _[key] = value
在给对象属性赋值的时候,内部原理就是操作对象的__dict__字典,所以直接操作属性字典就可以实现了
mm()
上面的结果:
a 正赋值
a 正赋值
a 正赋值。。。。
[Previous line repeated 494 more times]
File "D:\算法练习\algo-master\python\my_coding\__getattr__.py", line 31, in __setattr__
print(key, value)
可以看出上面产生了无线递归
因为 很显然内置函数 setattr(self, key ,value) 他触发的是 传入的第一个对象 self 的 --setattr-- 然后就明白了,又调用了 self 他自己,所以产生了无线递归
当我们是使用它们的时候注意,它们触发的方式
下面总结一下:
len -> --len--
getitem -> --getitem-- ( 字典三剑客都是)
getattr -> --getattr-- (其他两个也是这样)
str -> --str--
(其实 很多内置函数,都是触发了对象的默认操作标志,进而触发 魔法方法)
getattribute
这个其实和 --getattr-- 是有联系的,其实当调用属性时候,会先触发这个getattribute(如果你定义的话,没有定义 object 基类里面有这个,)返回属性值,如果没有这个属性 引发 AttriburteError,才会接着触发 --getattr--(正常是 会直接触发 --getattr--,但是基类里有 getattribute, 所以 --getattr-- 就轮到 娶不到属性时候调用了)
getattr(x, y) 内置函数等价于 x.y -> X.getattribute(y) -> --getattr--(y)
ok
def __getattribute__(self, *args, **kwargs): # real signature unknown
""" Return getattr(self, name). """
pass
因为
一般使用object.getattribute来避免无限调用. 因为直接
self. getattribute 就是无限递归了
def getattribute(self, item):
pass
网友评论