645 lines
15 KiB
Markdown
645 lines
15 KiB
Markdown
# 反射与双下方法
|
||
|
||
## 反射
|
||
|
||
- python面向对象中的反射:
|
||
- 指程序在**运行时**动态地获取对象(包括类、模块、实例等)的结构信息(如属性、方法、类型),并能动态地操作这些结构(如调用方法、修改属性、创建对象)的能力。简单来说,反射让程序能在运行时“自省”——了解自身的组成,并根据需要灵活调整行为。
|
||
- 反射的常见应用场景
|
||
- **动态配置加载**:根据配置文件(如JSON、YAML)动态设置对象属性。例如,通过`setattr`将配置中的`debug=True`映射到对象的`debug`属性。
|
||
- **插件化架构**:通过`importlib.import_module`动态加载插件模块,再用`getattr`获取插件类的实例并调用其方法。例如,电商系统可动态加载“限时抢购”“团购”等插件订单类。
|
||
- **通用工具函数**:编写适用于不同对象的通用工具。例如,`print_object_info`函数通过`dir`获取对象所有属性,再用`getattr`打印属性值。
|
||
- **ORM框架**:通过反射将数据库表的列映射到Python类的属性。例如,Django ORM通过反射获取模型的字段信息,动态生成SQL语句。
|
||
|
||
四个可以实现自省的函数
|
||
|
||
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
|
||
|
||
**对对象的反射**
|
||
|
||
```python
|
||
class Foo:
|
||
f = '类的静态变量'
|
||
def __init__(self,name,age):
|
||
self.name=name
|
||
self.age=age
|
||
|
||
def say_hi(self):
|
||
print('hi,%s'%self.name)
|
||
|
||
obj=Foo('张三',73)
|
||
|
||
# 检测是否含有某属性
|
||
print(hasattr(obj,'name'))
|
||
print(hasattr(obj,'say_hi'))
|
||
|
||
# 获取属性
|
||
n=getattr(obj,'name')
|
||
print(n)
|
||
func=getattr(obj,'say_hi')
|
||
func()
|
||
|
||
print(getattr(obj,'aaaaaaaa','不存在啊')) # 报错
|
||
|
||
# 设置属性
|
||
setattr(obj,'sb',True)
|
||
setattr(obj,'show_name',lambda self:self.name+'sb')
|
||
print(obj.__dict__)
|
||
print(obj.show_name(obj))
|
||
|
||
# 删除属性
|
||
delattr(obj,'age')
|
||
delattr(obj,'show_name')
|
||
# delattr(obj,'show_name111') # 不存在,则报错
|
||
|
||
print(obj.__dict__)
|
||
```
|
||
|
||
**对类的反射**
|
||
|
||
```python
|
||
class Foo(object):
|
||
staticField = "test"
|
||
|
||
def __init__(self):
|
||
self.name = '张三'
|
||
|
||
def func(self):
|
||
return 'func'
|
||
|
||
@staticmethod
|
||
def bar():
|
||
return 'bar'
|
||
|
||
|
||
print(getattr(Foo, 'staticField'))
|
||
print(getattr(Foo, 'func'))
|
||
print(getattr(Foo, 'bar'))
|
||
```
|
||
|
||
当前模块的反射
|
||
|
||
```python
|
||
import sys
|
||
|
||
def s1():
|
||
print('s1')
|
||
|
||
def s2():
|
||
print('s2')
|
||
|
||
this_module = sys.modules[__name__]
|
||
|
||
print(hasattr(this_module, 's1'))
|
||
print(getattr(this_module, 's2'))
|
||
```
|
||
|
||
其他模块的反射
|
||
|
||
程序目录:
|
||
module_test.py
|
||
test.py
|
||
|
||
当前文件:
|
||
test.py
|
||
|
||
```python
|
||
import module_test as obj
|
||
|
||
obj.test()
|
||
|
||
print(hasattr(obj,'test'))
|
||
|
||
getattr(obj,'test')()
|
||
```
|
||
|
||
举例:
|
||
|
||
使用反射前
|
||
|
||
```python
|
||
class User:
|
||
def login(self):
|
||
print('欢迎来到登录页面')
|
||
|
||
def register(self):
|
||
print('欢迎来到注册页面')
|
||
|
||
def save(self):
|
||
print('欢迎来到存储页面')
|
||
|
||
|
||
while 1:
|
||
choose = input('>>>').strip()
|
||
if choose == 'login':
|
||
obj = User()
|
||
obj.login()
|
||
|
||
elif choose == 'register':
|
||
obj = User()
|
||
obj.register()
|
||
|
||
elif choose == 'save':
|
||
obj = User()
|
||
obj.save()
|
||
```
|
||
|
||
用了反射之后
|
||
|
||
```python
|
||
class User:
|
||
def login(self):
|
||
print('欢迎来到登录页面')
|
||
|
||
def register(self):
|
||
print('欢迎来到注册页面')
|
||
|
||
def save(self):
|
||
print('欢迎来到存储页面')
|
||
|
||
|
||
user = User()
|
||
while 1:
|
||
choose = input('>>>').strip()
|
||
if hasattr(user, choose):
|
||
func = getattr(user, choose)
|
||
func()
|
||
else:
|
||
print('输入错误。。。。')
|
||
```
|
||
|
||
## 函数 vs 方法
|
||
|
||
### 通过打印函数(方法)名确定
|
||
|
||
```python
|
||
def func():
|
||
pass
|
||
|
||
print(func)
|
||
|
||
class A:
|
||
def func(self):
|
||
pass
|
||
|
||
print(A.func)
|
||
obj = A()
|
||
print(obj.func)
|
||
```
|
||
|
||
### 通过types模块验证
|
||
|
||
```python
|
||
from types import FunctionType
|
||
from types import MethodType
|
||
|
||
def func():
|
||
pass
|
||
|
||
class A:
|
||
def func(self):
|
||
pass
|
||
|
||
obj = A()
|
||
|
||
print(isinstance(func,FunctionType))
|
||
print(isinstance(A.func,FunctionType))
|
||
print(isinstance(obj.func,FunctionType))
|
||
print(isinstance(obj.func,MethodType))
|
||
```
|
||
|
||
### 静态方法是函数
|
||
|
||
```python
|
||
from types import FunctionType
|
||
from types import MethodType
|
||
|
||
|
||
class A:
|
||
|
||
def func(self):
|
||
pass
|
||
|
||
@classmethod
|
||
def func1(self):
|
||
pass
|
||
|
||
@staticmethod
|
||
def func2(self):
|
||
pass
|
||
|
||
|
||
obj = A()
|
||
|
||
# 静态方法其实是函数
|
||
print(isinstance(A.func2,FunctionType))
|
||
print(isinstance(obj.func2,FunctionType))
|
||
```
|
||
|
||
### 函数与方法的区别
|
||
|
||
那么,函数和方法除了上述的不同之处,我们还总结了一下几点区别。
|
||
|
||
1. 函数的是显式传递数据的。如我们要指明为len()函数传递一些要处理数据。
|
||
|
||
2. 函数则跟对象无关。
|
||
|
||
3. 方法中的数据则是隐式传递的。
|
||
|
||
4. 方法可以操作类内部的数据。
|
||
|
||
5. 方法跟对象是关联的。如我们在用strip()方法是,是不是都是要通过str对象调用,比如我们有字符串s,然后s.strip()这样调用。是的,strip()方法属于str对象。
|
||
|
||
我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。
|
||
|
||
在其他语言中,如Java中只有方法,C中只有函数,C++么,则取决于是否在类中
|
||
|
||
## 双下方法
|
||
|
||
### `__len__`
|
||
|
||
```python
|
||
class B:
|
||
def __len__(self):
|
||
return 666
|
||
|
||
b = B()
|
||
print(len(b)) # len 一个对象就会触发 __len__方法。
|
||
|
||
class A:
|
||
def __init__(self):
|
||
self.a = 1
|
||
self.b = 2
|
||
|
||
def __len__(self):
|
||
return len(self.__dict__)
|
||
a = A()
|
||
print(len(a))
|
||
```
|
||
|
||
### `__hash__`
|
||
|
||
```python
|
||
class A:
|
||
def __init__(self):
|
||
self.a = 1
|
||
self.b = 2
|
||
|
||
def __hash__(self):
|
||
return hash(str(self.a)+str(self.b))
|
||
a = A()
|
||
print(hash(a))
|
||
```
|
||
|
||
### `__str__`
|
||
|
||
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
|
||
|
||
```python
|
||
class A:
|
||
def __init__(self):
|
||
pass
|
||
def __str__(self):
|
||
return '张三'
|
||
a = A()
|
||
print(a)
|
||
print('%s' % a)
|
||
```
|
||
|
||
### `__repr__`
|
||
|
||
如果一个类中定义了__repr__方法,那么在repr(对象) 时,默认输出该方法的返回值。
|
||
|
||
```python
|
||
class A:
|
||
def __init__(self):
|
||
pass
|
||
def __repr__(self):
|
||
return '张三'
|
||
a = A()
|
||
print(repr(a))
|
||
print('%r'%a)
|
||
```
|
||
|
||
### `__call__`
|
||
|
||
对象后面加括号,触发执行。
|
||
|
||
注:构造方法__new__的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
|
||
|
||
```python
|
||
class Foo:
|
||
|
||
def __init__(self):
|
||
print('__init__')
|
||
|
||
def __call__(self, *args, **kwargs):
|
||
print('__call__')
|
||
|
||
|
||
obj = Foo() # 执行 __init__
|
||
obj() # 执行 __call__
|
||
```
|
||
|
||
### `__eq__`
|
||
|
||
```python
|
||
class A:
|
||
def __init__(self):
|
||
self.a = 1
|
||
self.b = 2
|
||
|
||
def __eq__(self,obj):
|
||
if self.a == obj.a and self.b == obj.b:
|
||
return True
|
||
a = A()
|
||
b = A()
|
||
print(a == b)
|
||
```
|
||
|
||
### `__del__`
|
||
|
||
析构方法,当对象在内存中被释放时,自动触发执行。
|
||
|
||
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
|
||
|
||
### `__new__`
|
||
|
||
- `__new__()` 方法是在类准备将自身实例化时调用。
|
||
- `__new__()` 方法始终都是类的静态方法,即使没有被加上静态方法装饰器。
|
||
- 通常来说,新式类开始实例化时,`__new__()`方法会返回cls(cls指代当前类)的实例,然后该类的`__init__()`方法作为构造方法会接收这个实例(即self)作为自己的第一个参数,然后依次传入`__new__()`方法中接收的位置参数和命名参数。
|
||
|
||
```python
|
||
class A:
|
||
def __init__(self):
|
||
self.x = 1
|
||
print('in init function')
|
||
def __new__(cls, *args, **kwargs):
|
||
print('in new function')
|
||
return object.__new__(A, *args, **kwargs)
|
||
|
||
a = A()
|
||
print(a.x)
|
||
```
|
||
|
||
单例模式
|
||
|
||
```python
|
||
class A:
|
||
__instance = None
|
||
def __new__(cls, *args, **kwargs):
|
||
if cls.__instance is None:
|
||
obj = object.__new__(cls)
|
||
cls.__instance = obj
|
||
return cls.__instance
|
||
```
|
||
|
||
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
|
||
**【采用单例模式动机、原因】**
|
||
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
|
||
如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
|
||
**【单例模式优缺点】**
|
||
**【优点】**
|
||
一、实例控制
|
||
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
|
||
二、灵活性
|
||
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
|
||
**【缺点】**
|
||
一、开销
|
||
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
|
||
二、可能的开发混淆
|
||
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
|
||
三、对象生存期
|
||
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用
|
||
|
||
### `__item__`系列
|
||
|
||
```python
|
||
class Foo:
|
||
def __init__(self,name):
|
||
self.name=name
|
||
|
||
def __getitem__(self, item):
|
||
print(self.__dict__[item])
|
||
|
||
def __setitem__(self, key, value):
|
||
self.__dict__[key]=value
|
||
print('赋值成功')
|
||
|
||
def __delitem__(self, key):
|
||
print('del obj[key]时,我执行')
|
||
self.__dict__.pop(key)
|
||
|
||
def __delattr__(self, item):
|
||
print('del obj.key时,我执行')
|
||
self.__dict__.pop(item)
|
||
|
||
f1=Foo('sb')
|
||
f1['age']=18
|
||
f1['age1']=19
|
||
del f1.age1
|
||
del f1['age']
|
||
f1['name']='mingzi'
|
||
print(f1.__dict__)
|
||
```
|
||
|
||
### 上下文管理器相关
|
||
|
||
`__enter__` `__exit__`
|
||
|
||
```python
|
||
class A:
|
||
|
||
def __init__(self, text):
|
||
self.text = text
|
||
|
||
def __enter__(self): # 开启上下文管理器对象时触发此方法
|
||
self.text = self.text + '您来啦'
|
||
return self # 将实例化的对象返回f1
|
||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): # 执行完上下文管理器对象f1时触发此方法
|
||
self.text = self.text + '这就走啦'
|
||
|
||
|
||
with A('大爷') as f1:
|
||
print(f1.text)
|
||
print(f1.text)
|
||
```
|
||
|
||
自定义文件管理器
|
||
|
||
```python
|
||
class Diycontextor:
|
||
def __init__(self, name, mode):
|
||
self.name = name
|
||
self.mode = mode
|
||
|
||
def __enter__(self):
|
||
print("Hi enter here!!")
|
||
self.filehander = open(self.name, self.mode)
|
||
return self.filehander
|
||
|
||
def __exit__(self,*args):
|
||
print("Hi exit here")
|
||
self.filehander.close()
|
||
|
||
|
||
with Diycontextor('config', 'r') as f:
|
||
for i in f:
|
||
print(i.strip())
|
||
```
|
||
|
||
案例
|
||
|
||
```python
|
||
class StarkConfig:
|
||
def __init__(self, num):
|
||
self.num = num
|
||
|
||
def run(self):
|
||
self()
|
||
|
||
def __call__(self, *args, **kwargs):
|
||
print(self.num)
|
||
|
||
|
||
class RoleConfig(StarkConfig):
|
||
def __call__(self, *args, **kwargs):
|
||
print(345)
|
||
|
||
def __getitem__(self, item):
|
||
return self.num[item]
|
||
|
||
|
||
v1 = RoleConfig('abcedf')
|
||
v2 = StarkConfig('2333')
|
||
print(v1[3])
|
||
## print(v2[2])
|
||
v1.run()
|
||
```
|
||
|
||
```python
|
||
class UserInfo:
|
||
pass
|
||
|
||
|
||
class Department:
|
||
pass
|
||
|
||
|
||
class StarkConfig:
|
||
def __init__(self, num):
|
||
self.num = num
|
||
|
||
def changelist(self, request):
|
||
print(self.num, request)
|
||
|
||
def run(self):
|
||
self.changelist(999)
|
||
|
||
|
||
class RoleConfig(StarkConfig):
|
||
def changelist(self, request):
|
||
print(666, self.num)
|
||
|
||
|
||
class AdminSite:
|
||
|
||
def __init__(self):
|
||
self._registry = {}
|
||
|
||
def register(self, k, v):
|
||
self._registry[k] = v
|
||
|
||
|
||
site = AdminSite()
|
||
site.register(UserInfo, StarkConfig)
|
||
# 1
|
||
obj = site._registry[UserInfo]()
|
||
|
||
# 2
|
||
# obj = site._registry[UserInfo](100)
|
||
obj.run()
|
||
```
|
||
|
||
```python
|
||
class UserInfo:
|
||
pass
|
||
|
||
class Department:
|
||
pass
|
||
|
||
class StarkConfig:
|
||
def __init__(self,num):
|
||
self.num = num
|
||
|
||
def changelist(self,request):
|
||
print(self.num,request)
|
||
|
||
def run(self):
|
||
self.changelist(999)
|
||
|
||
class RoleConfig(StarkConfig):
|
||
def changelist(self,request):
|
||
print(666,self.num)
|
||
|
||
|
||
class AdminSite:
|
||
|
||
def __init__(self):
|
||
self._registry = {}
|
||
|
||
def register(self,k,v):
|
||
self._registry[k] = v(k)
|
||
|
||
site = AdminSite()
|
||
site.register(UserInfo,StarkConfig)
|
||
site.register(Department,RoleConfig)
|
||
|
||
for k,row in site._registry.items():
|
||
row.run()
|
||
```
|
||
|
||
```python
|
||
class A:
|
||
list_display = []
|
||
|
||
def get_list(self):
|
||
self.list_display.insert(0, 33)
|
||
return self.list_display
|
||
|
||
|
||
s1 = A()
|
||
print(s1.get_list())
|
||
```
|
||
|
||
```python
|
||
class A:
|
||
list_display = [1, 2, 3]
|
||
def __init__(self):
|
||
self.list_display = []
|
||
def get_list(self):
|
||
self.list_display.insert(0, 33)
|
||
return self.list_display
|
||
|
||
|
||
s1 = A()
|
||
print(s1.get_list())
|
||
```
|
||
|
||
```python
|
||
class A:
|
||
list_display = []
|
||
|
||
def get_list(self):
|
||
self.list_display.insert(0,33)
|
||
return self.list_display
|
||
|
||
class B(A):
|
||
list_display = [11,22]
|
||
|
||
|
||
s1 = A()
|
||
s2 = B()
|
||
print(s1.get_list())
|
||
print(s2.get_list())
|
||
``` |