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

背景

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

属性取值和赋值过程

属性描述符

装饰器

本文最好会利用这些知识介绍:如何实现自定义的@staticmethod和@classmethod。

属性取值和赋值过程

d433e5370c0641cdb75901e16a775b54.png

一切皆是对象,类型也是对象。

对象包含一个__class__属性指向其所属类型。

对象包含一个__dict__属性指向其所包含的成员(属性和方法)。

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 __getattribute__(property) logic:

2

3 descripter = find first descripter in class and bases's dict(property)

4 if descripter:

5 return descripter.__get__(instance, instance.__class__)

6 else:

7 if value in instance.__dict__

8 return value

9

10 value = find first value in class and bases's dict(property)

11 if value is a function:

12 return bounded function(value)

13 else:

14 return value

15

16 raise AttributeNotFundedException

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 __setattr__(property, value)logic:

2

3 descripter = find first descripter in class and bases's dict(property)

4 if descripter:

5 descripter.__set__(instance, value)

6 else:

7 instance.__dict__[property] = value

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

赋值和取值的位置可能不同。

取值过程的伪代码第11行会返回绑定方法(我们在Javascript经常会让某个函数绑定到指定的作用域,和这个概念是一样的)。

取值和赋值过程都会先查找属性描述符,也就是说:属性描述的优先级最高,下文会介绍属性描述符。

属性描述符

什么是属性描述符?属性描述符就是一个类型,实现了三个魔法方法而已:__set__、__get__和__del__,属性描述符一般不会独立使用,必须存储在类型的__dict__中才有意义,这样才会参与到属性的取值和赋值过程,具体参见上文。

属性描述符

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 #属性描述符

2 class Descripter:

3 def __get__(self, instance, owner):

4 print(self, instance, owner)

5

6 def __set__(self, instance, value):

7 print(self, instance, value)

48304ba5e6f9fe08f3fa1abda7d326ab.png

测试代码

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class TestClass:

2 Des = Descripter()

3

4

5 def __getattribute__(self, name):

6 print("before __getattribute__")

7 return super(TestClass, self).__getattribute__(name)

8 print("after __getattribute__")

9

10 def __setattr__(self, name, value):

11 print("before __setattr__")

12 super(TestClass, self).__setattr__(name, value)

13 print("after __setattr__")

14

15 test1 = TestClass()

16 test2 = TestClass()

17

18 test1.Des = None

19 test2.Des

48304ba5e6f9fe08f3fa1abda7d326ab.png

输出结果

1 before __setattr__

2 <__main__.descripter object at> <__main__.testclass object at> None

3 after __setattr__

4 before __getattribute__

5 <__main__.descripter object at> <__main__.testclass object at>

结论

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

装饰器(AOP)

最基本的函数装饰器

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 print("\n最基本的函数装饰器\n")

2 def log(fun):

3 def return_fun(*args, **kargs):

4 print("开始输出日志")

5

6 fun(*args, **kargs)

7

8 print("结束输出日志")

9

10 return return_fun

11

12 @log

13 def say(message):

14 print(message)

15

16 say("段光伟")

17

18 print("\n等价方法\n")

19

20 def say(message):

21 print(message)

22

23 say = log(say)

24 say("段光伟")

48304ba5e6f9fe08f3fa1abda7d326ab.png

带参数的函数装饰器

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 print("\n带参数的函数装饰器\n")

2 def log(header, footer):

3 def log_to_return(fun):

4 def return_fun(*args, **kargs):

5 print(header)

6

7 fun(*args, **kargs)

8

9 print(footer)

10

11 return return_fun

12 return log_to_return

13

14 @log("开始输出日志", "结束输出日志")

15 def say(message):

16 print(message)

17

18 say("段光伟")

19

20 print("\n等价方法\n")

21

22 def say(message):

23 print(message)

24

25 say = log("开始输出日志", "结束输出日志")(say)

26 say("段光伟")

48304ba5e6f9fe08f3fa1abda7d326ab.png

最基本的类型装饰器

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 print("\n最基本的类型装饰器\n")

2 def flyable(cls):

3 def fly(self):

4 print("我要飞的更高")

5 cls.fly = fly

6

7 return cls

8

9 @flyable

10 class Man:

11 pass

12

13 man = Man()

14 man.fly()

15

16 print("\n等价方法\n")

17

18 class Man:

19 pass

20

21 Man = flyable(Man)

22

23 man = Man()

24 man.fly()

48304ba5e6f9fe08f3fa1abda7d326ab.png

带参数的类型装饰器

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 print("\n带参数的类型装饰器\n")

2 def flyable(message):

3 def flyable_to_return(cls):

4 def fly(self):

5 print(message)

6 cls.fly = fly

7

8 return cls

9 return flyable_to_return

10

11 @flyable("我要飞的更高")

12 class Man:

13 pass

14

15 man = Man()

16 man.fly()

17

18 print("\n等价方法\n")

19

20 class Man:

21 pass

22

23 Man = flyable("我要飞的更高")(Man)

24

25 man = Man()

26 man.fly()

48304ba5e6f9fe08f3fa1abda7d326ab.png

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

自己实现@staticmethod和@classmethod

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

代码

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 class MyStaticObject:

2 def __init__(self, fun):

3 self.fun = fun;

4

5 def __get__(self, instance, owner):

6 return self.fun

7

8 def my_static_method(fun):

9 return MyStaticObject(fun)

10

11 class MyClassObject:

12 def __init__(self, fun):

13 self.fun = fun;

14

15 def __get__(self, instance, owner):

16 def class_method(*args, **kargs):

17 return self.fun(owner, *args, **kargs)

18

19 return class_method

20

21 def my_class_method(fun):

22 return MyClassObject(fun)

23

24 class C(object):

25 """docstring for C"""

26

27 def test_instance_method(self):

28 print(self)

29

30 @staticmethod

31 def test_static_method(message):

32 print(message)

33

34 @my_static_method

35 def test_my_static_method(message):

36 print(message)

37

38 @classmethod

39 def test_class_method(cls):

40 print(cls)

41

42 @my_class_method

43 def test_my_class_method(cls):

44 print(cls)

45

46 print("\n实例方法测试")

47 c = C()

48 print(C.test_instance_method)

49 print(C.__dict__["test_instance_method"])

50 print(c.test_instance_method)

51 C.test_instance_method(c)

52 c.test_instance_method()

53

54 print("\n静态方法测试")

55 print(C.test_static_method)

56 print(C.__dict__["test_static_method"])

57 print(c.test_static_method)

58 C.test_static_method("静态方法测试")

59 c.test_static_method("静态方法测试")

60

61 print("\n自定义静态方法测试")

62 print(C.test_my_static_method)

63 print(C.__dict__["test_my_static_method"])

64 print(c.test_my_static_method)

65 C.test_my_static_method("自定义静态方法测试")

66 c.test_my_static_method("自定义静态方法测试")

67

68 print("\n类方法测试")

69 print(C.test_class_method)

70 print(C.__dict__["test_class_method"])

71 print(c.test_class_method)

72 C.test_class_method()

73 c.test_class_method()

74

75 print("\n自定义类方法测试")

76 print(C.test_my_class_method)

77 print(C.__dict__["test_my_class_method"])

78 print(c.test_my_class_method)

79

80 C.test_my_class_method()

81 c.test_my_class_method()

82

83 print("\n对象上的方法不会返回绑定方法,对象描述符也不会起作用")

84 def test(self):

85 print(self)

86

87 c.test = test

88

89 c.test("测试")

48304ba5e6f9fe08f3fa1abda7d326ab.png

结果

48304ba5e6f9fe08f3fa1abda7d326ab.png

1 实例方法测试

2

3

4 >

5 <__main__.c object at>

6 <__main__.c object at>

7

8 静态方法测试

9

10

11

12 静态方法测试

13 静态方法测试

14

15 自定义静态方法测试

16

17 <__main__.mystaticobject object at>

18

19 自定义静态方法测试

20 自定义静态方法测试

21

22 类方法测试

23 >

24

25 >

26

27

28

29 自定义类方法测试

30 .class_method at 0x01D5EDF8>

31 <__main__.myclassobject object at>

32 .class_method at 0x01D5EDF8>

33

34

35

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

37 测试

48304ba5e6f9fe08f3fa1abda7d326ab.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值