反射
反射主要用在网络编程中,
python面向对象的反射:通过字符串的形式操作对象相关的属性.python的一切事物都是对象.
反射就是通过字符串的形式,导入模块;通过字符串的形式,去模块寻找指定函数,并执行。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!
getattr()
查找getattr(object,name[,default]):使用字符串数据类型获取对象的方法和属性,注意name是字符串,如果存在则返回,不存在则打印出默认值,默认值可选。需要注意的是如果要运行这个对象的方法,则需要加括号。
class Manegeemnt: def __init__(self,name,sex,phone,mail): self.name=name self.sex=sex self.phone=phone self.mail=mail def creat_class(self): print("创建了一条班级信息") maneger=Manegeemnt("小雨","女","13813216396","xiaoyu@163.com") func=getattr(maneger,"name") #使用字符串的数据类型的变量名获取属性值。
print(func)
func=getattr(maneger,"creat_class")#得到的是方法的内存地址
func()
func=getattr(maneger,"mails","没有这条信息")
print(func)
结果:
小雨
创建了一条班级信息
没有这条信息
hasattr()
检查 hasattr(object,name[,default]):判断类中是否有这个属性如果存在返回True,否则False,在进行getattr前先判断有没有这个属性。一般他俩配合着使用。不要用try来替换它,我们能尽量少用try就少用,
多用些代码的方式来解决异常处理。
class Manegeemnt: def __init__(self,name,sex,phone,mail): self.name=name self.sex=sex self.phone=phone self.mail=mail def creat_class(self): print("创建了一条班级信息") import logging maneger=Manegeemnt("小雨","女","13813216396","xiaoyu@163.com") if hasattr(maneger,"sex1"): func=getattr(maneger,"sex1") print(func) else: logging.warning("没有这条信息")
结果:
WARNING:root:没有这条信息
setattr()
设置 setattr(obj,变量名,值):对对象设置属性,添加属性。还可以添加方法不过这个方法不常用,
class A: age=18 def __init__(self,name): self.name=name def walk(self): print("walking") a=A("小明") setattr(a,"country","中国") print(a.country)
结果:
中国
delattr()
删除 delattr(obj,变量名)
class Manegeemnt: def __init__(self,name,sex,phone,mail): self.name=name self.sex=sex self.phone=phone self.mail=mail def creat_class(self): print("创建了一条班级信息") import logging maneger=Manegeemnt("小雨","女","13813216396","xiaoyu@163.com") setattr(Manegeemnt,"country","china") print(Manegeemnt.country)
结果:
china
在sys.modules 里,如果我要运行当前的模块,那么当前的模块的名字就是__main__,所以可以跟筠模块名在sys.modules中得到他的模块对象、
a="bbb" import sys y=getattr(sys.modules["__main__"],"a") #利用模块名得到模块对象,注意在sys.modules 中的键是模块名,键值是模块对象。 print(y)
结果
bbb
另一种情况,如果我把上边的这个代码,剪贴到另外一个模块中,然后从本模块中导入,这时候再运行就会出错了,因为当前运行的程序名字中没有这段代码了,就不能用__main__,了,应该改为__name__.
范例:
a="bbb" import sys y=getattr(sys.modules[__name__],"a") #注意这里的__name__不能加引号,就是不能写成字符串,它的意思就是代表了这个程序的文件名。 print(y)
模块的反射
demo.py
def demo(): print("这是demo模块中的demo函数")
main.py
import demo ret=getattr(demo,"demo") ret()
应用场景:在django的CBV的源码中,在请求到了dispatch方法中是,基于反射根据请求方式的不同去调用不同的请求方法(比如post,get,put)
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
item系列
__setitem__ 对象可以像处理列表或字典一样使用索引运算符 []
,来设置对象的元素值。这使您可以像处理列表或字典一样操作对象的元素
def __setitem__(self, key, value): # 根据 key 设置相应的元素或属性的值为 value
__getitem__
是Python中的一个特殊方法,用于定义对象的索引访问行为。当您在一个自定义类中实现__getitem__
方法时,您可以像处理列表或字典一样使用索引运算符[]
来访问对象的元素或属性。这使您可以定义自己的数据容器或数据类型,并以一种自然的方式访问其中的元素。
__delitem__
在类中, 只能用字典的方式,赋值,取值,删除值,才会调用响应的函数,用类自己的方法来,赋值,取值,删除值,不生效.
class A: def __init__(self): print("init.....") def __setitem__(self, key, value): print("set....") def __getitem__(self, item): print("get....") def __delitem__(self, key): print("del....") b=A() #调用init函数 b["f"]=3 #调用set函数 b["f"] #调用get函数 del b["f"] #调用delete函数
结果:
init..... set.... get.... del....
范例二:
lass testsetandget: kk = {} def __getitem__(self, key): return self.kk[key] def __setitem__(self, key, value): self.kk[key] = value print("1111") a=testsetandget() a["first"]=1 print(a.kk) a.__setitem__("你好",2) a.__getitem__("你好")
结果:
1111 {'first': 1} 1111
面向对象内置函数
1. issubclass(子类,父类) 判断一个类是不是另一个类的父类
class A: pass class B(A): pass print(issubclass(B,A))
结果:
True
2.isinstance(对象,已知类型) ,判断这个对象是不是已知类型
范例一:判断这个对象是不是A类实例化出来
class A: pass class B(A): pass c=B() print(isinstance(c,A))
结果:
True
范例二判断这个变量是不是字符串?
f="fdfd" print(isinstance(f,str))
结果:
True
__getattr__和__setattr__
__getattr__(self,item)
__getattr__(self,item) :拦截点号运算。当对象(注意不能是类,类会报错)对未定义的属性名称(只能是属性不能是方法,虽然调用方法时会调用该方法,但是程序也会报错)点号运算时,就会用属性名作为字符串调用这个方法,其中item就是调用的属性名称字符串。如果继承树可以找到该属性,则不调用此方法.
注意:访问不存在的属性,才会触发 __getattr__方法
class Foo: def __init__(self,name): self.name=name def __getattr__(self, item): print("item",item) print("你好啊") a=Foo("xiao") a.age
结果:
item age
你好啊
其实这个方法是在看restfromwork中的源码中看到的
#a在APIview中这样调用的, authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) #但是在APISettings类中并没有这个DEFAULT_AUTHENTICATION_CLASSES属性 class APISettings(object): def __getattr__(self, attr): if attr not in self.defaults: raise AttributeError("Invalid API setting: '%s'" % attr) try: # Check if present in user settings val = self.user_settings[attr] except KeyError: # Fall back to defaults val = self.defaults[attr] # Coerce import strings into classes if attr in self.import_strings: val = perform_import(val, attr) # Cache the result self._cached_attrs.add(attr) setattr(self, attr, val) return val
应用场景:
rest_framework中的认证组件中在导入默认认证类的时候出现。
当
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
就调用了__getattr__
def __getattr__(self, attr): if attr not in self.defaults: raise AttributeError("Invalid API setting: '%s'" % attr) try: # Check if present in user settings val = self.user_settings[attr] except KeyError: # Fall back to defaults val = self.defaults[attr] # Coerce import strings into classes if attr in self.import_strings: val = perform_import(val, attr) # Cache the result self._cached_attrs.add(attr) setattr(self, attr, val) return val
应用场景2:在rest_framework中的APIView中的dispatch方法中
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: # 这里的request是封装后的request并不是原来的request,新的request中并没有method方法 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
这里的request是封装后的request并不是原来的request,新的request中并没有method方法
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) #这里并没有method方法
虽然request类中没有method方法,但是有__getattr__方法
def __getattr__(self, attr): """ If an attribute does not exist on this instance, then we also attempt to proxy it to the underlying HttpRequest object. """ try: return getattr(self._request, attr) # self._request是老的request except AttributeError: return self.__getattribute__(attr)
由此可见新的request是调用的老的request中的method方法
__getattribute__
与 __getattr__
不同的是,只要访问对象中的属性,__getattribute__
特殊方法就会被触发,即便该属性已经在 __dict__
字典中了。
class A: def __init__(self, name): self.name = name def __getattribute__(self, item): print("执行__getattribute__") return 100 a = A("alex") print(a.name) print("------") print(a.age)
结果:
执行__getattribute__ 100 ------ 执行__getattribute__ 100
内置函数 hasattr()
和 getattr()
也会触发上述的特殊方法:
print(hasattr(a, "iii")) print("------") print(getattr(a, "123")) #结果 执行__getattribute__ True ------ 执行__getattribute__ 100
值得注意的是,操作 __getattribute__
挂钩势必会使得属性访问的开销增加,降低程序的效率,因此我们应只在适当的时候使用该方法。比如,在访问数据库模型的属性时,我们可使用 __getattribute__
验证事务状态。
一般情况下不用改方法,一般用__getattr__替代
这个函数在drf源码中出现
__setattr__()
如果类自定义了__setattr__
方法,当通过实例化赋值或属性赋值时,就会调用__setattr__
。
常规的对实例属性赋值,被赋值的属性和值会存入实例属性字典__dict__
中。
class ClassA(object): def __init__(self, classname): self.classname = classname def __setattr__(self, name, value): # self.name = value # 如果还这样调用会出现无限递归的情况 print('invoke __setattr__') insA = ClassA('ClassA') # __init__中的self.classname调用__setattr__。 # invoke __setattr__ insA.tag = 'insA' # invoke __setattr__
结果:
invoke __setattr__ invoke __setattr__