学习了Javascript才知道原来属性的取值和赋值操作访问的“位置”可能不同、还有词法作用域这个东西,这也是我学习任何一门语言会注意的两个知识点,Python的作用域和Javascript几乎一致,这里就不做解释,本文重点介绍一下三个概念:
本文最好会利用这些知识介绍:如何实现自定义的@staticmethod和@classmethod。
- __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
什么是属性描述符?属性描述符就是一个类型,实现了三个魔法方法而已:__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__,具体自己可以测试一下。
最基本的函数装饰器
- 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就不成问题了,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开发的插件。