魔法方法是 Python 内置方法, 不需要我们手动调用, 它存在的目的是给 解释器 调用的. 比如我们在写 "1 + 1 " 的时候, 这个 "+ " 就会自动调用内置的魔法方法 "__ add__" . 几乎每个魔法方法, 都有一个对应的内置函数或运算符. 当我们使用这些方法去操作数据时, 解释器会自动调用这些对应的魔法方法. 也可以理解为, 重写内置函数, 如果改变的话.
具体的魔法方法等. 可以去看 内置的 builtins.py 这个接口文档.
内置函数对应魔法方法: 如 next() 对应 __ next __ , 类名() 对应 __ call __ 等这样的映射关系 ....
彻底阐明, 在 Pyth
关于 object 和 type 想必大家在编程实践中, 是经常会接触到这两个 关键字.
首先来看 type.
估计有 90% 的小伙伴都会以为这个神奇的内置函数 type 是一个函数, 用来判断对象类型的, 然后来看看源码:
通常我们是这样来使用 type 的.
>>> type(123)
<class 'int'>
>>> type("abc")
<class 'str'>
>>> type([1,2,3])
<class 'list'>
>>> type((1,2,3))
<class 'tuple'>
>>> type({"name":"youge", "age":18})
<class 'dict'>
>>> type(abs)
<class 'builtin_function_or_method'>
>>> def my_func(): pass
...
>>> type(my_func)
<class 'function'> # 函数也是个类
>>> import numpy as np
>>> arr = np.array([1,2])
>>> type(arr)
<class 'numpy.ndarray'>
>>> class A(): pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> type(A)
<class 'type'>
>>> type(int)
<class 'type'>
>>> type(dict)
<class 'type'>
>>> type(object) # object 的类型是type
<class 'type'>
>>> type(type) # type 的 type 就是type 类自身呀
<class 'type'>
于是呢, 可以得到这样一个初步结论, 或者是错误矫正:
在Python3中, 所有的类都默认继承了 object, 可能大家平时也没太注意, 但我以前用 Python2 的时候, 是需要手动去继承 objecet 的, 当时还区分什么, 新式类, 旧式类这样的东西...
class A(object):
pass
class B:
pass
# 二者是一样的, 在Python3中, 会自动默认继承 object, 可以不写.
都有一个共识, object 是所有类的 基类 (base class). 然后我们所有的类都去继承它, 在进行各种操作. 这没有什么问题, 然后突然想来一句 灵魂发问: object 的基类是什么 ? 我一度认为是 type, 然后打脸了.
>>> object.__bases__
()
>>> type.__bases__
(<class 'object'>,)
>>> object.__subclasses__
<built-in method __subclasses__ of type object at 0x0000000056DDB5C0>
object 竟然没有基类, 而 type 是继承于 object 的. 基于万物皆对象, 对于万物, 我纵观中国哲学史, 对于世界的本质探讨, 我认为第一人还是 老子, 在 <<道德经>> 里中对于天地的概述:
道生一, 一生二, 二生三, 三生万物
这样给 object 来拔高一看, 类似 object 类 就是 "一", 而 type 类 就是 "阴阳", 咱们平时用的就是 "三" .
也不知这样解释是否合理, 但我相信这个点基本是 get到了.
其实就是一些常用的内置函数嘛, 在类设计中呢, 可以可以通过改写这些 魔法方法 来帮助我们完成一些事情 , 就类似于临时性改掉了 Python 的源代码. 听上去还挺酷的.
当然只有先明白了 "万物皆对象" 的原理后呢, 再来理解这些内置函数会很 自然. 当然还忽略了一个前提, 默认大家对面向对象是非常熟悉哦. 如果连什么是类, 什么是实例, 什么是方法, 函数, 这些都不明白话, 本篇的探讨就可能不太适合了...
当然这里也只是举了一些常用的而已, 更多的可以去参考python的源码, builtins.py 有更多的介绍.
功能: 定制类创建的过程, 在 创建类对象时被触发, 它是一个静态方法(不需要self), 第一个参数 cls 是传一个类, 而不是实例对象 self, 因此, __ new __ 是在 __ init __ 之前调用的.
class Cat:
def __new__(cls, *args, **kwargs):
print("__new__ is called")
if __name__ == '__main__':
cat = Cat()
# out
__new__ is called
那当 __ new __ 和 __ init __ 一起的时候, 会如何呢?
class Cat:
def __new__(cls, *args, **kwargs):
print("__new__ is called")
def __init__(self):
print("__init__ is called")
if __name__ == '__main__':
cat = Cat()
# output
__new__ is called
发现, __ init __ 没有被执行. __ new __ 方法通常会返回该类的一个实例, 或其他类的实例, 于是就会跳过 __ init __ 方法 . 假如我还是想再 执行 __ init __ , 则 返回父类的 __ new __ 即可, 相当于 "白做了一遍"
因为 __ new __ 已经构造好对象, 就不在需要 __ init __ 了
class Cat:
def __init__(self, name):
self.name = name
print("__init__ is called")
def __new__(cls, *args, **kwargs):
print("__new__ is called")
return super().__new__(cls)
if __name__ == '__main__':
cat = Cat("youge")
print('cat name is:', cat.name)
# output
__new__ is called
__init__ is called
cat name is: youge
看上去似乎没啥用, 举个比较经典的栗子, 单例模式. 就是一个类, 值让其 创键一个实例, 和执行一次初始化.
class Singleton:
# 定义两个类属性, 状态的判断
__instance = None
__init = False
def __new__(cls, *args, **kwargs):
# 如果没有被创建则创建, 创建了则直接返回该实例呀
if cls.__instance is None:
print("create the instance")
cls.__instance = super().__new__(cls)
return cls.__instance
def __init__(self):
# 第一次先开门,进来
if not self.__init:
print("init the instance here")
# 再关门, 永远关的那种
self.__init = True
if __name__ == '__main__':
s1 = Singleton()
s2 = Singleton()
print(id(s1), id(s2))
# output
create the instance
init the instance here, please.
# s1 跟 s2 是一个实例.
2249920782008 2249920782008
这样就是所谓的 单例模式 呀, 我想, 应用场景, 应该非常直观了, 举个最近的栗子, 目前让弄一个小工具, 将Excel 数据转为为 一种特殊的格式, 发送到 Tableau 服务器. 准备加一个图片认证, 就防止是机器瞎几攻击服务器嘛, 然后这个功能封装成一个类, 每次实例化再调用方法即可....
但考虑到, 其功能是固定的, 不能每次用户登录, 都来一遍创建实例, 其实只需要创建一次 就ok了, 这就用到了 单例模式了呀...
功能: 初始化实例对象. 即在创建实例时 被自动触发 .
class Cat:
def __init__(self, name, gender):
self.name = name
self.gender = gender
# __init__ 会在实例创建时被调用,.
cat1 = Cat("youge", 'M')
print(cat1.name)
# output
youge
功能: 在写 "对象()" 时会自动被触发. 实现将类的实例行为, 表现一个函数.
应用, 嗯, 有在做代码优化的时候, 将 一个函数 作为参数 传递给另外的函数. 可以做一些参数校验或是, 改变值之类.
class Plane:
def __init__(self, name, x, y):
self.x = x
self.y = y
self.name = name
def __call__(self, x, y):
print("__call__ is called")
self.x = x
self.y = y
if __name__ == '__main__':
p = Plane("youge", 20, 30)
print("pos:", p.x, p.y)
# call 方法能将 instance 作为 function 使用
p(50, 100)
print("pos:", p.x, p.y)
# output
pos: 20 30
__call__ is called
pos: 50 100
另外, 之前在整装饰器的时候, 用类实现 装饰器 其实就有用到 call 方法的呀.
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("--- checking ---")
return self.func(*args, **kwargs)
@MyDecorator
def check_2019_nCov(name):
print(f"{name} is very healty")
if __name__ == '__main__':
check_2019_nCov("youge")
# output
--- checking ---
youge is very healty
功能: 打印一个对象的信息, 随便打印啥都可以, 在 print(实例对象名) 时被触发.
class Cat:
def __init__(self, name, age):
self.age = age
self.name = name
def __str__(self):
signature = f" my name is {self.name}, my age is {self.age}"
return signature
if __name__ == '__main__':
cat1 = Cat("youge", 18)
print(cat1)
# 没有实现 __str__ 方法就打印对象名而已.
print(Cat)
# ouptput
my name is youge, my age is 1
<class '__main__.Cat'>
功能: 当删除一个对象时, 会被自动触发.
值得注意的是 "del obj" 在 Python 中, 最为牛逼的一个点是 自动垃圾回收. 前面已经讲了无数次, Python 变量的本质是 指针 了. 即地址的引用, 只有当变量指向的对象的 引用计数为0 时, 变量才会被真正回收.
class Cat:
def __del__(self):
print("__del__ is called")
if __name__ == '__main__':
cat = Cat()
del cat # 会自动触发 __del__ 方法
# output
__del__ is called
说明一点是, 如果定义了 __ init __ 方法, 则会 先 会调用 __ init __ , 删除对象的时候调用 __ del __ 方法
class Cat:
def __init__(self, name):
self.name = name
print("__init__ is called")
def __del__(self):
print("__del__ is called")
if __name__ == '__main__':
cat = Cat("youge") # 自动触发 __init__ 方法
del cat # 自动触发 __del__ 方法
# output
__init__ is called
__del__ is called
当然还有一些常用的魔法方法, 篇幅有限嘛, 毕竟, 关键的是掌握这些, 方法在何种情景下使用是最为关键的. 比如, __ new __ 是静态方法, 可用在 单例设计上, __ init __ 用在类的初始化上, 大家都懂哈; __ call __ 可以用在 装饰器上 ..... 等等.
下篇再接着整几个, 我突然来劲了还.
原文:https://www.cnblogs.com/chenjieyouge/p/12257585.html