反射

反射主要用在网络编程中,

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__

 

posted on 2017-11-24 20:30  程序员一学徒  阅读(261)  评论(0编辑  收藏  举报