2025年3月25日 星期二 甲辰(龙)年 月廿四 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

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

时间:11-23来源:作者:点击数:39

背景

学习了Javascript才知道原来属性的取值和赋值操作访问的“位置”可能不同、还有词法作用域这个东西,这也是我学习任何一门语言会注意的两个知识点,Python的作用域和Javascript几乎一致,这里就不做解释,本文重点介绍一下三个概念:

  1. 属性取值和赋值过程
  2. 属性描述符
  3. 装饰器

本文最好会利用这些知识介绍:如何实现自定义的@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

根据取值和赋值过程的逻辑可以得出以下结论

  1. 赋值和取值的位置可能不同。
  2. 取值过程的伪代码第11行会返回绑定方法(我们在Javascript经常会让某个函数绑定到指定的作用域,和这个概念是一样的)。
  3. 取值和赋值过程都会先查找属性描述符,也就是说:属性描述的优先级最高,下文会介绍属性描述符。

属性描述符

什么是属性描述符?属性描述符就是一个类型,实现了三个魔法方法而已:__set__、__get__和__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'>
  • 对象上的方法不会返回绑定方法,对象描述符也不会起作用
  • 测试

备注

Python的学习和总结就到一段落了,继续弄PHP,不过还会写一篇如何用Python开发Sublime插件的教程,开发一个方便PHP开发的插件。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门