Python:高级主题使用技巧(属性取值和赋值过程、属性描述符、装饰器)

37 阅读4分钟

Python的作用域和Javascript几乎一致,这里就不做解释,本文重点介绍一下三个概念:

属性取值和赋值过程 属性描述符 装饰器 本文最好会利用这些知识介绍:如何实现自定义的@staticmethod和@classmethod。

属性取值和赋值过程

一切皆是对象,类型也是对象。 对象包含一个__class__属性指向其所属类型。 对象包含一个__dict__属性指向其所包含的成员(属性和方法)。 取值过程(下面是伪代码)

 __getattribute__(property) logic:
 
 descripter = find first descripter in class and bases's dict(property)
 if descripter:
     return descripter.__get__(instance, instance.__class__)
 else:
     if value in instance.__dict__
         return value
 
     value = find first value in class and bases's dict(property)
     if value is a function:
         return bounded function(value)
     else:
         return value
 
 raise AttributeNotFundedException

赋值过程(下面是伪代码)

__setattr__(property, value)logic:

descripter = find first descripter in class and bases's dict(property)
if descripter:
    descripter.__set__(instance, value)
else:
    instance.__dict__[property] = value

根据取值和赋值过程的逻辑可以得出以下结论 赋值和取值的位置可能不同。 取值过程的伪代码第11行会返回绑定方法(我们在Javascript经常会让某个函数绑定到指定的作用域,和这个概念是一样的)。 取值和赋值过程都会先查找属性描述符,也就是说:属性描述的优先级最高,下文会介绍属性描述符。 属性描述符 什么是属性描述符?属性描述符就是一个类型,实现了三个魔法方法而已:setget__和__del,属性描述符一般不会独立使用,必须存储在类型的__dict__中才有意义,这样才会参与到属性的取值和赋值过程,具体参见上文。

属性描述符

#属性描述符
class Descripter:
    def __get__(self, instance, owner):
        print(self, instance, owner)

    def __set__(self, instance, value):
        print(self, instance, value)

测试代码

 class TestClass:
     Des = Descripter()
 
 
     def __getattribute__(self, name):
         print("before __getattribute__")
         return    super(TestClass, self).__getattribute__(name)
         print("after __getattribute__")
 
     def __setattr__(self, name, value):
         print("before __setattr__")
         super(TestClass, self).__setattr__(name, value)
         print("after __setattr__")
 
 test1 = TestClass()
 test2 = TestClass()
 
 test1.Des = None
 test2.Des

输出结果

 before __setattr__
 <__main__.Descripter object at 0x01D9A030> <__main__.TestClass object at 0x01D9A090> None
 after __setattr__
 before __getattribute__
 <__main__.Descripter object at 0x01D9A030> <__main__.TestClass object at 0x01D9A0B0> <class '__main__.TestClass'>

结论

类型的多个实例共享同一个属性描述符实例,属性描述符的优先级高于实例的__dict__,具体自己可以测试一下。

装饰器(AOP) 最基本的函数装饰器

 print("\n最基本的函数装饰器\n")
 def log(fun):
     def return_fun(*args, **kargs):
         print("开始输出日志")
 
         fun(*args, **kargs)
 
         print("结束输出日志")
 
     return return_fun
 
 @log
 def say(message):
     print(message)
 
 say("段伟")
 
 print("\n等价方法\n")
 
 def say(message):
     print(message)
 
 say = log(say)
 say("段伟")


带参数的函数装饰器


 print("\n带参数的函数装饰器\n")
 def log(header, footer):
     def log_to_return(fun):
         def return_fun(*args, **kargs):
             print(header)
 
             fun(*args, **kargs)
 
             print(footer)
 
         return return_fun
     return log_to_return
 
 @log("开始输出日志", "结束输出日志")
 def say(message):
     print(message)
 
 say("段光伟")
 
 print("\n等价方法\n")
 
 def say(message):
     print(message)
 
 say = log("开始输出日志", "结束输出日志")(say)
 say("段伟")


最基本的类型装饰器


 print("\n最基本的类型装饰器\n")
 def flyable(cls):
     def fly(self):
         print("我要飞的更高")
     cls.fly = fly
     
     return cls
 
 @flyable
 class Man:
     pass
 
 man = Man()
 man.fly()
 
 print("\n等价方法\n")
 
 class Man:
     pass
 
 Man = flyable(Man)
 
 man = Man()
 man.fly()


带参数的类型装饰器


 print("\n带参数的类型装饰器\n")
 def flyable(message):
     def flyable_to_return(cls):
         def fly(self):
             print(message)
         cls.fly = fly
         
         return cls
     return flyable_to_return
 
 @flyable("我要飞的更高")
 class Man:
     pass
 
 man = Man()
 man.fly()
 
 print("\n等价方法\n")
 
 class Man:
     pass
 
 Man = flyable("我要飞的更高")(Man)
 
 man = Man()
 man.fly()

备注:可以使用多个装饰器,不过要保证签名的装饰器也是返回的一个方法或类型。

自己实现@staticmethod和@classmethod 理解了属性的取值和赋值过程,开发自定义@staticmethod和@classmethod就不成问题了,let up do it!

代码


 class MyStaticObject:
     def __init__(self, fun):
         self.fun = fun;
 
     def __get__(self, instance, owner):
         return self.fun
 
 def my_static_method(fun):
     return MyStaticObject(fun)
 
 class MyClassObject:
     def __init__(self, fun):
         self.fun = fun;
 
     def __get__(self, instance, owner):
         def class_method(*args, **kargs):
             return self.fun(owner, *args, **kargs)
 
         return class_method
 
 def my_class_method(fun):
     return MyClassObject(fun)
 
 class C(object):
     """docstring for C"""

     def test_instance_method(self):
         print(self)
 
     @staticmethod
     def test_static_method(message):
         print(message)
 
     @my_static_method
     def test_my_static_method(message):
         print(message)
 
     @classmethod
     def test_class_method(cls):
         print(cls)
 
     @my_class_method
     def test_my_class_method(cls):
         print(cls)
 
 print("\n实例方法测试")
 c = C()
 print(C.test_instance_method)
 print(C.__dict__["test_instance_method"])
 print(c.test_instance_method)    
 C.test_instance_method(c)
 c.test_instance_method()
 
 print("\n静态方法测试")
 print(C.test_static_method)
 print(C.__dict__["test_static_method"])
 print(c.test_static_method)
 C.test_static_method("静态方法测试")
 c.test_static_method("静态方法测试")
 
 print("\n自定义静态方法测试")
 print(C.test_my_static_method)
 print(C.__dict__["test_my_static_method"])
 print(c.test_my_static_method)
 C.test_my_static_method("自定义静态方法测试")
 c.test_my_static_method("自定义静态方法测试")
 
 print("\n类方法测试")
 print(C.test_class_method)
 print(C.__dict__["test_class_method"])
 print(c.test_class_method)
 C.test_class_method()
 c.test_class_method()
 
 print("\n自定义类方法测试")
 print(C.test_my_class_method)
 print(C.__dict__["test_my_class_method"])
 print(c.test_my_class_method)
 
 C.test_my_class_method()
 c.test_my_class_method()
 
 print("\n对象上的方法不会返回绑定方法,对象描述符也不会起作用")
 def test(self):
     print(self)
 
 c.test = test
 
 c.test("测试")

结果

 实例方法测试
 <function C.test_instance_method at 0x01D3D8A0>
 <function C.test_instance_method at 0x01D3D8A0>
 <bound method C.test_instance_method of <__main__.C object at 0x01D8B5B0>>
 <__main__.C object at 0x01D8B5B0>
 <__main__.C object at 0x01D8B5B0>
 
 静态方法测试
 <function C.test_static_method at 0x01D69108>
 <staticmethod object at 0x01D8B4F0>
 <function C.test_static_method at 0x01D69108>
 静态方法测试
 静态方法测试
 
 自定义静态方法测试
 <function C.test_my_static_method at 0x01D69078>
 <__main__.MyStaticObject object at 0x01D8B510>
 <function C.test_my_static_method at 0x01D69078>
 自定义静态方法测试
 自定义静态方法测试
 
 类方法测试
 <bound method type.test_class_method of <class '__main__.C'>>
 <classmethod object at 0x01D8B530>
 <bound method type.test_class_method of <class '__main__.C'>>
 <class '__main__.C'>
 <class '__main__.C'>
 
 自定义类方法测试
 <function MyClassObject.__get__.<locals>.class_method at 0x01D5EDF8>
 <__main__.MyClassObject object at 0x01D8B550>
 <function MyClassObject.__get__.<locals>.class_method at 0x01D5EDF8>
 <class '__main__.C'>
 <class '__main__.C'>

对象上的方法不会返回绑定方法,对象描述符也不会起作用 测试