面向对象之:反射, 双下方法
面向对象之:反射 双下方法
反射
概念
- 程序可以访问、检测和修改它本身状态或行为的一种能力 (自省)
python中的反射
- python面向对象中的反射:通过字符串的形式操作对象相关的属性。
- python中的一切事物都是对象(都可以使用反射)
四个实现 自省的函数(obj,str)
- 下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
- hasattr 检测 是否 有某属性
- 格式 :
hasattr(obj,"str")
# 结果为 True or False
- 格式 :
- getattr 获取属性
- 格式 :
getattr(obj,"str",default / 默认返回值)
default在属性不存在时设定 / 属性不存在,不设置返回值会报错
- 格式 :
- setattr 设置 / 修改 属性(attr)
- 格式 :
setattr(obj,"str",value)
- 格式 :
- delattr 删除属性
- 格式 :
delattr(obj,"str")
- 格式 :
对对象的反射
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def hi_hello(self):
print("hi,%s"%self.name)
obj = Foo("alex",18)
# 检测是否含有某属性
print(hasattr(obj,"name")) # True
print(hasattr(obj,"hi_hello")) # True
# 获取属性
print(getattr(obj,"name")) # alex
func = getattr(obj,"hi_hello")
func() # hi,alex
getattr(obj,"hi_hello")() # hi,alex
print(getattr(obj,'aaaaaaaa','不存在啊')) # 不存在
# 用hasattr去判断 ------------------------
if hasattr(obj,"hi_hello"):
getattr(obj,"hi_hello")() # hi,alex
#设置属性
setattr(obj,'sb',True) # 对象属性中添加了 "sb" = True 的属性
setattr(obj,'show_name',lambda self:self.name+'sb') # 添加方法
print(obj.__dict__) # {'name': 'alex', 'age': 18, 'sb': True, 'show_name': <function <lambda> at 0x00000127653DC1E0>}
print(obj.show_name(obj)) # alexsb
#删除属性
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show_name111') # 不存在,则报错
print(obj.__dict__)
对类的反射
class Foo(object):
staticField = "old boy"
def __init__(self):
self.name = 'wupeiqi'
def func(self):
return 'func'
@staticmethod
def bar():
return 'bar'
print(getattr(Foo, 'staticField')) # old boy
print(getattr(Foo, 'func')) # 方法地址
print(getattr(Foo, 'bar')) # 静态属性地址
当前模块的反射
import sys
def s1():
print('s1')
def s2():
print('s2')
this_module = sys.modules[__name__]# 获取到当前模块这个对象 所有内容 以模块地址体现
hasattr(this_module, 's1')
getattr(this_module, 's2')
其他模块的反射
#一个模块中的代码
def test():
print('from the test')
"""
程序目录:
module_test.py
index.py
当前文件:
index.py
"""
# 另一个模块中的代码
import module_test as obj
#obj.test()
print(hasattr(obj,'test')) # True
getattr(obj,'test')() # from the test
反射的应用
示例 : 网页登录功能
# 不用反射==============================
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()
# 应用反射==============================
class Auth:
blog_garden = [("login","请登录"),("register","请注册"),("exit","退出")]
def login(self):
print("login")
def register(self):
print("register")
def exit(self):
exit("exit")
while 1:
obj = Auth()
for num,option in enumerate(obj.blog_garden,1):
print(num,option[1])
choice_num = input("请输入选择序号:").strip()
try:
if int(choice_num) > 0:
hasattr(obj,obj.blog_garden[int(choice_num)-1][0])
getattr(obj,obj.blog_garden[int(choice_num)-1][0])()
else:
print("输入有误,请重新输入")
except Exception :
print("输入有误,请重新输入") # 归一化设计 方便管理 维护
函数vs方法
通过打印函数(方法名)确定
def func():
pass
print(func) # <function func at 0x00000260A2E690D0>
class A:
def func(self):
pass
print(A.func) # <function A.func at 0x0000026E65AE9C80>
obj = A()
print(obj.func) # <bound method A.func of <__main__.A object at0x00000230BAD4C9E8>> 绑定方法
通过types模块验证
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
静态方法是函数 *
from types import FunctionType
class A:
def func(self):
pass
@classmethod
def func1(self):
pass
@staticmethod
def func2(self):
pass
obj = A()
# 静态方法其实是函数
print(isinstance(A.func2,FunctionType)) # True
print(isinstance(obj.func2,FunctionType)) # True
函数与方法的区别
- (1)函数的是显式传递数据的。如我们要指明为len()函数传递一些要处理数据。
(2)函数则跟对象无关。
(3)方法中的数据则大多是隐式传递的。
(4)方法可以操作类内部的数据。
(5)方法跟对象是关联的。如我们在用strip()方法是,是不是都是要通过str对象调用,比如我们有字符串s,然后s.strip()这样调用。是的,strip()方法属于str对象。 - 我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。
在其他语言中,如Java中只有方法,C中只有函数,C++么,则取决于是否在类中。
双下方法
定义
- 双下方法是特殊方法,解释器提供的 格式:
__方法名__
的具有特殊意义的方法,双下方法主要是python源码程序员使用的,我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。
调用
- 不同的双下方法有不同的触发方式,就好比盗墓时触发的机关一样,不知不觉就触发了双下方法,例如:
__init__
__iter__
将对象变成 可迭代对象
class A:
def __iter__(self): # 将一个对象设置成了一个可迭代对象
yield 1
yield 2
obj = A() # 他现在已经变成可迭代对象
print(next(obj)) # 报错!
for i in obj:
print(i) # 1 2
iterator1 = iter(obj)
print(next(iter(obj))) # 1
print(next(iter(obj))) # 1
print(list(obj)) # [1,2]
print(obj) # 对象地址
__len__
一个对象之所以可以使用len()函数,根本原因是这个对象从属的类中有__len__
方法
class B:
def __len__(self):
print(666)
b = B()
len(b) # len 一个对象就会触发 __len__方法。
class A:
def __init__(self,a,b):
self.a = 1
self.b = 2
def __len__(self):
print('触发__len__方法')
return len(self.__dict__) # 测出对象的属性个数
a = A("alex",12)
print(len(a))
__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)) # 测出a+b的哈希值
__str__
***
与__repr__
相似 但__str__
优先级高
- 如果一个类中定义了
__str__
方法,那么在打印 对象 时,默认输出该方法的返回值 - 格式化输出也会触发
__str__
- 打印输出实例会触发
__str__
class A:
def __init__(self):
pass
def __str__(self):
return '太白' # 返回值可以自定义(重写str)
a = A()
print(a) # 太白
print('%s' % a) # 太白
__repr__
***
- 如果一个类中定义了
__repr__
方法,那么在repr(对象) 时,默认输出该方法的返回值 - 格式化输出也会触发
__repr__
class A:
def __init__(self):
pass
def __repr__(self):
return '太白'
a = A()
print(repr(a)) # 太白
print('%r'%a) # 太白
__init__
***
- 双下方法
__init__
的执行是由创建对象触发的,即:对象 = 类名()
__call__
***
实例化对象 () / 类名()()触发
- 对象后面加括号,触发执行
- 注:双下方法
__init__
的执行是由创建对象触发的,即:对象 = 类名() ;而对于__call__
方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__ 结果是 '__call__'
Foo()() # 执行 __call__ 结果是 '__call__'
__eq__
- 当两个对象进行比较时 , 触发
__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) # True
__del__
析构方法 ***
析构方法,当对象在内存中被释放时,自动触发执行
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
__new__
单例模式 ***
- 类名()先触发
__new__
并且将类名自动传给cls __new__
比__init__
优先级高,先执行__new__
- 利用父类object类的
__new__
产生一个对象空间 - 将这个对象空间返回给了A() 即obj
class A:
def __init__(self):
self.x = 1
print('in init function')
def __new__(cls, *args, **kwargs):
print('in new function')
return object.__new__(cls, *args, **kwargs)
a = A()
单例模式 :创建对象唯一性 / 只创建一个对象空间
- 即对象地址一直是一个
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__
对象进行类似于字典的操作
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
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')
print(f1.__dict__)
f1['age']=18
print(f1.__dict__)
f1['age1']=19
print(f1.__dict__)
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
上下文管理器相关
__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: # f1 = A('大爷')
print(f1.text) # 大爷您来啦
print(f1.text) # 大爷您来啦这就走啦
自定义文件管理器
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, *para):
print( "Hi exit here")
self.filehander.close()
with Diycontextor('py_ana.py', 'r') as f:
for i in f:
print(i)