反射与双下方法
1. 类的反射
程序对自己内部代码的一种自省方式。
定义:通过字符串取操作对象的方式。
可应用于实例对象、类、本模块、其他模块。 带有点 . 的方式都可用反射方法。
四种方法:
1. hasattr(object, name) # 判断、检测
2. getattr(object, name) # 获取
3. setattr(object, name) # 设置
4. delattr(object, name) # 删除属性
1.1 实例对象
class A:
country = '中国'
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
print('in A func')
obj = A("meet", 18)
print(hasattr(obj, 'name')) # True 检测是否有name属性
print(hasattr(obj, 'sex')) # False
print(getattr(obj, 'name')) # 获取obj的name属性
print(getattr(obj, 'sex', None))
#指定获取不到,返回None
getattr(obj,'func')()
# 调用obj的func方法,自动将obj地址传入参数中
if hasattr(obj, 'func'):
getattr(obj, 'func')()
setattr(obj, 'sex', '男') # 给obj增加 sex='男' 属性
deltattr(obj,'name') # 删除obj的name属性
print(obj.__dict__)
1.2 类
# 通过类名
class A:
country = '中国'
def __init__(self,name,age):
self.name = name
self.age = age
def func(self):
print('in A func')
if hasattr(A, 'country')
print(getattr(A, 'country')) # 中国
if hasattr(A, 'func'):
obj = A("meet", 18)
getattr(A, 'func')(obj)
#或:getattr(obj,'func')()
1.3 其他模块 -- 其他.py文件
# 名字为 tbjx.py 的文件
name = "太白金星"
def func():
print("这是func函数")
class C:
area = '北京'
def __init__(self, name):
self.name = name
def func1(self):
print('in C func1')
# 本文件
import tbjx # 导入
print(getattr(tbjx,'name')) # 获取变量name
getattr(tbjx, 'func')() # 执行func函数
obj = getattr(tbjx,'C')('meet') # 实例化对象
print(getattr(tbjx.C, 'area')) # 查找C类的area属性
ret = getattr(tbjx,'C') # 实例化对象后执行类的func1方法
obj = ret('meet')
getattr(obj, 'func')()
1.4 本模块 -- 当前.py文件
import sys
sys.modules[__name__] # 获取当前py文件的模块对象
def func1():
print('in func1')
def func2():
print('in func2')
def func3():
print('in func3')
def func4():
print('in func4')
import sys
content = input('请输入:') # 输入函数名
ret = sys.modules[__name__] # 获取本文件模块的对象
getattr(ret, content)()
class User:
user_list = [('login', '登录'), ('register', '注册'), ('save', '存储')]
def login(self):
print('欢迎来到登录页面')
def register(self):
print('欢迎来到注册页面')
def save(self):
print('欢迎来到存储页面')
while True:
obj = User()
for i in enumerate(obj.user_list, 1):
print(f'{i[0]}.{i[1][1]}')
choose = input('请输入序号:').strip()
getattr(obj, obj.user_list[int(choose)-1][0])()
2. 函数与方法的区别
2.1区别的方法:
方法一:
通过打印函数名的方式。 通过类名调用的方法是函数,通过对象调用类中的实例方法,是方法。
方法二:借助模块
from types import FunctionType
from types import MethodType
from types import FunctionType
from types import MethodType
def func():
pass
class A:
def func(self):
pass
obj = A()
print(isinstance(func,FunctionType)) # True
print(isinstance(A.func,FunctionType)) # True
print(isinstance(obj.func,FunctionType)) # False
print(isinstance(obj.func,MethodType)) # True
2.2 总结
总结:
函数都是显性传参,方法都是隐形传参;
类方法是一个方法,静态方法是一个函数。
扩展:Java中只有方法,C中只有函数,C++么,则取决于是否在类中。
3. 特殊的双下方法
原本是开发python这个语言的程序员用的,源码中使用的。不能轻易使用、使用。
3.01 __len__ (len一下对象就触发)
class B:
def __len__(self):
print(666)
b = B()
len(b) # len 一个对象就会触发 __len__方法。
# 返回a对象的属性的个数
class A:
def __init__(self):
self.a = 1
self.b = 2
def __len__(self):
return len(self.__dict__)
a = A()
print(len(a))
3.02 __hash__ (hash一下对象就触发)
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)) # 若本类没有,就会从object父类找
3.03 __str__ (打印对象触发、str()也会触发)
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'姓名:{self.name} 年龄:{self.age}'
# 必须是 return 字符串
a = A('meet', 18)
print(a) # 触发
3.04 __repr__ (打印对象和repr()都会触发)
class A:
def __init__(self):
pass
def __repr__(self):
return '太白'
a = A()
print(repr(a))
print('%r'%a) # 优先级低于str
3.05 __call__ (对象名(),触发)
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
3.06 __eq__ (打印对象触发)
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) # 触发
3.07 __del__ (析构方法)
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
3.08 __new__ (构造方法 类名()触发)
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)
# 类名(),触发object类的__new__方法,产生了一个对象空间并返回到 类名(), 再触发__init__方法,封装对象属性。
单例模式:
一个类只允许实例化一个对象。 将对象唯一化(延用同一个对象空间)。节省内存,减少开销。
方便对实例个数的控制并节约系统空间。 如:电脑的任务管理器。
class A:
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
obj = object.__new__(cls)
cls.__instance = obj
return cls.__instance
obj = A()
print(obj)
obj1 = A()
print(obj1) # 两次地址相同
3.09 _item_ 类似字典操作触发
# 1. __getitem__ 对对象进行类似于字典取值操作时触发
class A:
def __init__(self, name):
self.name = name
def __getitem__(self, item):
return self.__dict__[item]
obj = A('meet')
print(obj['name']) # meet
# 2.__setitem__ 对对象进行类似字典的增改触发
class A:
def __init__(self, name):
self.name = name
def __setitem__(self, key, value):
self.__dict__[key] = value
obj = A('meet')
obj['age'] = 18
print(obj.__dict__) # {'name': 'meet', 'age': 18}
# 3.__delitem__ 对对象进行类似字典的删除
class A:
def __init__(self, name):
self.name = name
def __delitem__(self, key):
self.__dict__.pop(key)
obj = A('meet')
del obj['name']
print(obj.__dict__) # {}
3.10 _delattr_ del 对象.属性 会触发
# 删除对象属性,找不到属性会报错;并不是真正的删除,而是触发__delattr__方法,delattr中有写入删除的功能。
class A:
def __init__(self, name):
self.name = name
def __delattr__(self, item):
self.__dict__.pop(item)
obj = A('meet')
del obj.name
print(obj.__dict__) # {}
3.11 _enter_ _exit_ : with 上下文管理
# 实例化对象的第二种方法: 必须基于__enter__、__exit__方法。
# 再进f1前执行enter、退出后执行exit
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) # 大爷您来啦,这就走啦
3.12 _iter_
# 在类中加入__iter__(self),将对象转换为可迭代对象,for循环可触发
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def __iter__(self):
for j in range(10):
yield j
obj = A('meet', 18)
# print(dir(obj))
for i in obj:
print(i) # 打印 0 1 2 ...