Python:高级主题之(属性取值和赋值过程、属性描述符、装饰器)
Python:高级主题之(属性取值和赋值过程、属性描述符、装饰器)
背景
学习了Javascript才知道原来属性的取值和赋值操作访问的“位置”可能不同、还有词法作用域这个东西,这也是我学习任何一门语言会注意的两个知识点,Python的作用域和Javascript几乎一致,这里就不做解释,本文重点介绍一下三个概念:
- 属性取值和赋值过程
- 属性描述符
- 装饰器
本文最好会利用这些知识介绍:如何实现自定义的@staticmethod和@classmethod。
属性取值和赋值过程

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

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

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

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

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

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)

测试代码

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

输出结果

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)
最基本的函数装饰器

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("段光伟")

带参数的函数装饰器

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("段光伟")

最基本的类型装饰器

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()

带参数的类型装饰器

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()

备注:可以使用多个装饰器,不过要保证签名的装饰器也是返回的一个方法或类型。
自己实现@staticmethod和@classmethod
理解了属性的取值和赋值过程,开发自定义@staticmethod和@classmethod就不成问题了,let up do it!
代码

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("测试")

结果

1 实例方法测试
2 <function C.test_instance_method at 0x01D3D8A0>
3 <function C.test_instance_method at 0x01D3D8A0>
4 <bound method C.test_instance_method of <__main__.C object at 0x01D8B5B0>>
5 <__main__.C object at 0x01D8B5B0>
6 <__main__.C object at 0x01D8B5B0>
7
8 静态方法测试
9 <function C.test_static_method at 0x01D69108>
10 <staticmethod object at 0x01D8B4F0>
11 <function C.test_static_method at 0x01D69108>
12 静态方法测试
13 静态方法测试
14
15 自定义静态方法测试
16 <function C.test_my_static_method at 0x01D69078>
17 <__main__.MyStaticObject object at 0x01D8B510>
18 <function C.test_my_static_method at 0x01D69078>
19 自定义静态方法测试
20 自定义静态方法测试
21
22 类方法测试
23 <bound method type.test_class_method of <class '__main__.C'>>
24 <classmethod object at 0x01D8B530>
25 <bound method type.test_class_method of <class '__main__.C'>>
26 <class '__main__.C'>
27 <class '__main__.C'>
28
29 自定义类方法测试
30 <function MyClassObject.__get__.<locals>.class_method at 0x01D5EDF8>
31 <__main__.MyClassObject object at 0x01D8B550>
32 <function MyClassObject.__get__.<locals>.class_method at 0x01D5EDF8>
33 <class '__main__.C'>
34 <class '__main__.C'>
35
36 对象上的方法不会返回绑定方法,对象描述符也不会起作用
37 测试

备注
Python的学习和总结就到一段落了,继续弄PHP,不过还会写一篇如何用Python开发Sublime插件的教程,开发一个方便PHP开发的插件。
Python:高级主题之(属性取值和赋值过程、属性描述符、装饰器)的更多相关文章
- spring+hibernate实体类注解详解(非原创) + cascade属性取值
@Entity //继承策略.另一个类继承本类,那么本类里的属性应用到另一个类中 @Inheritance(strategy = InheritanceType.JOINED ) @Table(nam ...
- jquery通过name属性取值的方法
jquery通过name属性取值的方法//$("input[name='imgtitle']").val();//这个只能取到第一个的值//通过each函数取得所有input的值v ...
- js动态参数作为Object的属性取值
js动态参数作为Object的属性取值var myObj = {"a":1,"b":2};var a = 'a';myObj[a] 就可以获取到 属性a的值了
- jquery select取值,赋值操作
select">jquery select取值,赋值操作 一.获取Select 获取select 选中的 text : $("#ddlRegType").find( ...
- [前端 2]常用的JQuery和Dom页面取值与赋值
导读:书到用时方恨少,需要基础知识的时候,才悔恨自己没有总结学习好.前段时间调了好长时间的页面,突然发现自己之前不怎么在意的取值和赋值,真的是自己一个很薄弱的地方,有时候查半天都找不到一个对的,现在用 ...
- html常用标签的取值和赋值操作
我们在html页面当中,面对各种各样的标签,经常需要处理取值和赋值的问题,下面,就把常见的一些html标签元素的取值和赋值操作进行总结整理,以后备用. 1.button:改变button按钮上面的值, ...
- jQuery 表单元素取值与赋值方法总结
一.普通文本框的赋值与取值 1.1.1赋值 <h2>jQuery 表单元素取值与赋值方法总结</h2> <input type="text" clas ...
- Visual Studio 2017中使用正则修改部分内容 如何使用ILAsm与ILDasm修改.Net exe(dll)文件 C#学习-图解教程(1):格式化数字字符串 小程序开发之图片转Base64(C#、.Net) jquery遍历table为每一个单元格取值及赋值 。net加密解密相关方法 .net关于坐标之间一些简单操作
Visual Studio 2017中使用正则修改部分内容 最近在项目中想实现一个小工具,需要根据类的属性<summary>的内容加上相应的[Description]特性,需要实现的效 ...
- select2多选设置select多选,select2取值和赋值
select2设置select多选,select2取值和赋值,作为筛选条件的时候,取值相对简单,把选中的id值转为字符串传给后端查询,查询之后会刷新页面,为了在下拉框中显示刚刚选中的值,就需要给sel ...
随机推荐
- Swift语言指南(三)--语言基础之整数和浮点数
原文:Swift语言指南(三)--语言基础之整数和浮点数 整数 整数指没有小数的整数,如42,-23.整数可以是有符号的(正数,零,负数),也可以是无符号的(正数,零). Swift提供了8,16,3 ...
- UVa 11121 - Base -2
题目:计算以-2为基数的数的表示. 分析:数论.写出不同位数能表示的数字区间就能够找到规律. 长度为1:[1,1]: 长度为2:[-2,-1]: 长度为3:[2,5]: 观察发现,区间长度增长为1,2 ...
- sql server drop talbe 自动删除关联的外键 ,权限体系(一)
if object_id('Proc_DropTableWithFK') is not null begin drop proc dbo.Proc_DropTableWithFK end GO ) a ...
- sql语句查询列的说明
SELECT C.name,value FROM sys.columns C INNER JOIN sys.tables T ON C.object_id = T.object_idINNER JOI ...
- 网页动态切换母版页(MasterPage)
原文:网页动态切换母版页(MasterPage) 是否可以变更网页的母版页(MasterPage)呢?某.aspx在创建时,已经附加入某一母版页(MasterPage)了,现需要.aspx动态变更母版 ...
- 安卓MonkeyRunner源码分析之启动
在工作中因为要追求完成目标的效率,所以更多是强调实战,注重招式,关注怎么去用各种框架来实现目的.但是如果一味只是注重招式,缺少对原理这个内功的了解,相信自己很难对各种框架有更深入的理解. 从几个月前开 ...
- JUnit4教程-高速入口
前言 大学刚学java当听说JUnit该,单元测试框架,使用非常简单的测试框架,JUnit测试测试框架将更加方便,easy.编写測试代码也是简单.明了,功能强大.今天我给大家简单分享一下最新JUnit ...
- Yii中CDbCriteria常用总结
Yii的Active Recorder包装了很多. 特别是把SQL中 把where,order,limit,IN/not IN,like等常用短句都包含进CDbCriteria这个类中去,这样整个代码 ...
- userAgent,JS这么屌的用户代理,你造吗?——判断浏览器内核、浏览器、浏览器平台、windows操作系统版本、移动设备、游戏系统
1.识别浏览器呈现引擎 为了不在全局作用域中添加多余变量,这里使用单例模式(什么是单例模式?)来封装检测脚本.检测脚本的基本代码如下所示: var client = function() { var ...
- Extjs grid分页多选记忆功能
很多同事在用extjs grid做分页的时候,往往会想用grid的多选功能来实现导出Excel之类的功能(也就是所谓的多选记忆功能),但在选选择下一页的时候 上一页选中的已经清除 这是因为做分页的时候 ...