目录
自己实现@staticmethod和@classmethod
背景
学习了Javascript才知道原来属性的取值和赋值操作访问的“位置”可能不同、还有词法作用域这个东西,这也是我学习任何一门语言会注意的两个知识点,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经常会让某个函数绑定到指定的作用域,和这个概念是一样的)。
- 取值和赋值过程都会先查找属性描述符,也就是说:属性描述的优先级最高,下文会介绍属性描述符。
属性描述符
什么是属性描述符?属性描述符就是一个类型,实现了三个魔法方法而已:__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
输出结果
1 before __setattr__ 2 <__main__.Descripter object at 0x01D9A030> <__main__.TestClass object at 0x01D9A090> None 3 after __setattr__ 4 before __getattribute__ 5 <__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开发的插件。