python的描述符
A. python 如何在进程间传递文件描述符
libancillary可以做到这点(其实也是socket):
http://www.normalesup.org/~george/comp/libancillary/
不过它是C代码所以你需要些binding,比如ctypes:
http://www.python.net/crew/theller/ctypes/tutorial.html
我想你都问的这么深了就不用我写示范了吧。
B. Python 开发中有哪些高级技巧
1. 善用迭代器
迭代器在很多语言里面都有,而在 Python
里适当的场景用迭代器会非常的“爽”。一来因为迭代器每次产生一个对象,适当使用能有效节省内存;二来它能达到部分“延迟计算”的效果。除此以外,因为
Generator (yield 关键字)和 Generator Expression 的存在,有时候使用迭代器能提升代码可读性。
举
例,itertools.islice((calculate_for_value(v) for v in values), 0, 12) 能够只在
[0, 12) 范围内计算,而且是延迟计算的,即迭代到了那个对象才去计算。又如 any(i % 3 == 0 for i in
numbers) 能够找出 numbers 里第一个能被 3 整除的值,因为里面是个 Generator
Expression(迭代器的一种),所以找出以后 any 函数就会立即返回,并不需要对整个 numbers 列表计算 i % 3。
顺带推荐下这个库 erikrose/more-itertools ,里面包含了很多实用的迭代器函数,是对标准库 itertools 的一个很不错的补充。
2. 善用描述符(Descriptor)
Python
的描述符是对“属性”的抽象,一个描述符定义成类属性以后,能够控制这个类的实例上同名实例属性的 get、set、delete 行为,比
__getattr__ 这样的实例级 magic method 有更细的粒度,并且更容易复用。这个文档有简单的描述 Descriptor HowTo Guide ,可见 Python 的“实例方法”、@property 全由它实现,一些第三方库也有用到(例如 SQLAlchemy 的 Column、WTForms 的 Field 乃至 Python 3.4 新增的 enum.Enum 类型)。
利用描述符特性,可以在业务代码中实现一些非常方便的定制,例如可以自己实现一个能缓存返回值的 cached_property(也可以不用自己实现,直接用 Werkzeug 的)。
3. 尽量不要用反射技巧去 fight with language
C. python中用fileno没返回文件描述符,请问是怎么回事
这个是函数,你要用fileno();而不是fileno
D. python中w、r表示什么意思
文件读写就是一种常见的IO操作。
文件读写操作步骤
不同的编程语言读写文件的操作步骤大体都是一样的,都分为以下几个步骤:
1)打开文件,获取文件描述符;
2)操作文件描述符--读/写;
3)关闭文件。
相关推荐:《Python教程》
文件打开模式:
r+、w+和a+都可以实现对文件的读写,那么他们有什么区别呢?
r+会覆盖当前文件指针所在位置的字符,如原来文件内容是"Hello,World",打开文件后写入"hi"则文件内容会变成"hillo, World"。
w+与r+的不同是,w+在打开文件时就会先将文件内容清空,不知道它有什么用。
a+与r+的不同是,a+只能写到文件末尾(无论当前文件指针在哪里)。
E. python连接Oracle报错:DatabaseError: ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
jdbc:oracle:thin:@10.20.206.47:1521:shl","
中10.20.206.47替换成实例名
F. python 描述符 作用
首先要说,这个descriptor真的不怎么常用。。。所以题主暂时不必为不理解而着急。
然后,没有必要再贴一遍文档出来,既然题主知道这个概念肯定是看过文档的。
所以,只能说题主放宽心,等用python时间长了比较熟练了以后,也许会碰到使用描述符的场景,等那时候再去看应该就不难理解了。
G. python 函数是不是描述符
在Python中,访问一个属性的优先级顺序按照如下顺序:
1.类属性
2.数据描述符
3.实例属性
4.非数据描述符
5.__getattr__()方法。这个方法的完整定义如下所示:
[python] view plain
def __getattr__(self,attr) :#attr是self的一个属性名
pass;
先来阐述下什么叫数据描述符。
数据描述符是指实现了__get__,__set__,__del__方法的类属性(由于Python中,一切皆是对象,所以你不妨把所有的属性也看成是对象)
PS:个人觉得这里最好把数据描述符等效于定义了__get__,__set__,__del__三个方法的接口。
阐述下这三个方法:
__get__的标准定义是__get__(self,obj,type=None),它非常接近于JavaBean的get
第一个函数是调用它的实例,obj是指去访问属性所在的方法,最后一个type是一个可选参数,通常为None(这个有待于进一步的研究)
例如给定类X和实例x,调用x.foo,等效于调用:
type(x).__dict__["foo"].__get__(x,type(x))
调用X.foo,等效于调用:
type(x).__dict__["foo"].__get__(None,type(x))
第二个函数__set__的标准定义是__set__(self,obj,val),它非常接近于JavaBean的set方法,其中最后一个参数是要赋予的值
第三个函数__del__的标准定义是__del__(self,obj),它非常接近Java中Object的Finailize()方法,指
Python在回收这个垃圾对象时所调用到的析构函数,只是这个函数永远不会抛出异常。因为这个对象已经没有引用指向它,抛出异常没有任何意义。
接下来,我们来一一比较这些优先级.
首先来看类属性
[python] view plain
# -*- coding:utf-8 -*-
'''''
Created on 2013-3-29
@author: naughty
'''
class A(object):
foo=3
print A.foo
a=A()
print a.foo
a.foo=4
print a.foo
print A.foo
上面这段代码的输出如下:
3
3
4
3
从输出可以看到,当我们给a.foo赋值的时候,其实与类实例的那个foo是没有关系的。a.foo=4 这句话给a对象增加了一个属性叫foo。其值是4。
最后两个语句明确的表明了,我们输出a.foo和A.foo的值,他们是不同的。
但是为什么a=A()语句后面的print
a.foo输出了3呢?这是因为根据搜索顺序找到了类属性。当我们执行a.foo=4的时候,我们让a对象的foo属性指向了4这个对象。但是并没有改变
类属性foo的值。所以最后我们print A.foo的时候,又输出了3。
[python] view plain
# -*- coding:utf-8 -*-
'''''
Created on 2013-3-29
@author: naughty
'''
class A(object):
foo=3
a=A()
a.foo=4
print a.foo
del a.foo
print a.foo
上面的代码,我给a.foo赋值为4,在输出一次之后就del了。两次输出,第一次输出的是a对象的属性。第二次是类属性。不是说类属性的优先级比
实例属性的高吗。为啥第一次输出的是4而不是3呢?还是上面解释的原因。因为a.foo与类属性的foo只是重名而已。我们print
a.foo的时候,a的foo指向的是4,所以输出了4。
------------------------------------
然后我们来看下数据描述符这一全新的语言概念。按照之前的定义,一个实现了__get__,__set__,__del__的类都统称为数据描述符。我们来看下一个简单的例子。
[python] view plain
# -*- coding:utf-8 -*-
'''''
Created on 2013-3-29
@author: naughty
'''
class simpleDescriptor(object):
def __get__(self,obj,type=None):
pass
def __set__(self,obj,val):
pass
def __del__(self,obj):
pass
class A(object):
foo=simpleDescriptor()
print str(A.__dict__)
print A.foo
a=A()
print a.foo
a.foo=13
print a.foo
上面例子的输出结果如下:
[plain] view plain
{'__dict__': <attribute '__dict__' of 'A' objects>, '__mole__': '__main__', 'foo': <__main__.simpleDescriptor object at 0x005511B0>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
None
None
None
从输出结果看出,print语句打印出来的都是None。这说明a.foo都没有被赋值内容。这是因为__get__函数的函数体什么工作都没有做。直接是pass。此时,想要访问foo,每次都没有返回内容,所以输出的内容就是None了。
[python] view plain
# -*- coding:utf-8 -*-
'''''
Created on 2013-3-29
@author: naughty
'''
class simpleDescriptor(object):
def __get__(self,obj,type=None):
return "hi there"
def __set__(self,obj,val):
pass
def __del__(self,obj):
pass
class A(object):
foo=simpleDescriptor()
print str(A.__dict__)
print A.foo
a=A()
print a.foo
a.foo=13
print a.foo
把__get__函数实现以下,就可以得到如下输出结果:
[plain] view plain
{'__dict__': <attribute '__dict__' of 'A' objects>, '__mole__': '__main__', 'foo': <__main__.simpleDescriptor object at 0x00671190>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
hi there
hi there
hi there
为了加深对数据描述符的理解,看如下例子:
[python] view plain
# -*- coding:utf-8 -*-
'''''
Created on 2013-3-29
@author: naughty
'''
class simpleDescriptor(object):
def __init__(self):
self.result = None;
def __get__(self, obj, type=None) :
return self.result - 10;
def __set__(self, obj, val):
self.result = val + 3;
print self.result;
def __del__(self, obj):
pass
class A(object):
foo = simpleDescriptor();
a = A();
a.foo = 13;
print a.foo;
上面代码的输出是
16
6
第一个16为我们在对a.foo赋值的时候,人为的将13加上3后作为foo的值,第二个6是我们在返回a.foo之前人为的将它减去了10。
所以我们可以猜测,常规的Python类在定义get,set方法的时候,如果无特殊需求,直接给对应的属性赋值或直接返回该属性值。如果自己定义类,并且继承object类的话,这几个方法都不用定义。
-----------------
在这里看一个题外话。
看代码
[python] view plain
# -*- coding:utf-8 -*-
'''''
Created on 2013-3-29
@author: naughty
'''
class simpleDescriptor(object):
def __init__(self):
self.result = None;
def __get__(self, obj, type=None) :
return self.result - 10;
def __set__(self, obj, val):
if isinstance(val,str):
assert False,"int needed! but get str"
self.result = val + 3;
print self.result;
def __del__(self, obj):
pass
class A(object):
foo = simpleDescriptor();
a = A();
a.foo = "13";
print a.foo;
上面代码在__set__ 函数中检查了参数val,如果val是str类型的,那么要报错。这就实现了我们上一篇文章中要实现的,在给属性赋值的时候做类型检查的功能。
-----------------------------------------------
下面我们来看下实例属性和非数据描述符。
[python] view plain
# -*- coding:utf-8 -*-
'''''
Created on 2013-3-29
@author: naughty
'''
class B(object):
foo = 1.3
b = B()
print b.__dict__
b.bar = 13
print b.__dict__
print b.bar
上面代码输出结果如下:
{}
{'bar': 13}
13
那么什么是非数据描述符呢?
简单的说,就是没有实现get,set,del三个方法的所有类。
让我们任意看一个函数的描述:
def call():
pass
执行print dir(call)会得到如下结果:
[plain] view plain
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__mole__', '__name__', '__new__', '__rece__', '__rece_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
先看下dir的帮助。
dir列出给定对象的属性或者是从这个对象能够达到的对象。
回到print dir(call)方法的输出,看到,call方法,有输出的那么多个属性。其中就包含了__get__函数。但是却没有__set__和__del__函数。所以所有的类成员函数都是非数据描述符。
看一个实例数据掩盖非数据描述符的例子:
[python] view plain
'''''
Created on 2013-3-29
@author: naughty
'''
class simpleDescriptor(object):
def __get__(self,obj,type=None) :
return "get",self,obj,type
class D(object):
foo=simpleDescriptor()
d=D()
print d.foo
d.foo=15
print d.foo
看输出:
('get', <__main__.simpleDescriptor object at 0x02141190>,
<__main__.D object at 0x025CAF50>, <class '__main__.D'>)
15
可见,实例数据掩盖了非数据描述符。
如果改成数据描述符,那么就不会被覆盖了。看下面:
[python] view plain
'''''
Created on 2013-3-29
@author: naughty
'''
class simpleDescriptor(object):
def __get__(self,obj,type=None) :
return "get",self,obj,type
def __set__(self,obj,type=None) :
pass
def __del__(self,obj,type=None) :
pass
class D(object):
foo=simpleDescriptor()
d=D()
print d.foo
d.foo=15
print d.foo
代码的输出如下:
[plain] view plain
('get', <__main__.simpleDescriptor object at 0x01DD1190>, <__main__.D object at 0x0257AF50>, <class '__main__.D'>)
('get', <__main__.simpleDescriptor object at 0x01DD1190>, <__main__.D object at 0x0257AF50>, <class '__main__.D'>)
由于是数据描述符,__set __函数体是pass,所以两次输出都是同样的内容。
最后看下__getatrr__方法。它的标准定义是:__getattr__(self,attr),其中attr是属性名
H. 如何正确地使用Python的属性和描述符
关于@property装饰器
在Python中我们使用@property装饰器来把对函数的调用伪装成对属性的访问。
那么为什么要这样做呢?因为@property让我们将自定义的代码同变量的访问/设定联系在了一起,同时为你的类保持一个简单的访问属性的接口。
举个栗子,假如我们有一个需要表示电影的类:
1
2
3
4
5
6
7
8
class Movie(object):
def __init__(self, title, description, score, ticket):
self.title = title
self.description = description
self.score = scroe
self.ticket = ticket
你开始在项目的其他地方使用这个类,但是之后你意识到:如果不小心给电影打了负分怎么办?你觉得这是错误的行为,希望Movie类可以阻止这个错误。 你首先想到的办法是将Movie类修改为这样:
Python
1
2
3
4
5
6
7
8
class Movie(object):
def __init__(self, title, description, score, ticket):
self.title = title
self.description = description
self.ticket = ticket
if score < 0:
raise ValueError("Negative value not allowed:{}".format(score))
self.score = scroe
但这行不通。因为其他部分的代码都是直接通过Movie.score来赋值的。这个新修改的类只会在__init__方法中捕获错误的数据,但对于已经存在的类实例就无能为力了。如果有人试着运行m.scrore= -100,那么谁也没法阻止。那该怎么办?
Python的property解决了这个问题。
我们可以这样做
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Movie(object):
def __init__(self, title, description, score):
self.title = title
self.description = description
self.score = score
self.ticket = ticket
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0:
raise ValueError("Negative value not allowed:{}".format(score))
self.__score = score
@score.deleter
def score(self):
raise AttributeError("Can not delete score")
这样在任何地方修改score都会检测它是否小于0。
property的不足
对property来说,最大的缺点就是它们不能重复使用。举个例子,假设你想为ticket字段也添加非负检查。下面是修改过的新类:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Movie(object):
def __init__(self, title, description, score, ticket):
self.title = title
self.description = description
self.score = score
self.ticket = ticket
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0:
raise ValueError("Negative value not allowed:{}".format(score))
self.__score = score
@score.deleter
def score(self):
raise AttributeError("Can not delete score")
@property
def ticket(self):
return self.__ticket
@ticket.setter
def ticket(self, ticket):
if ticket < 0:
raise ValueError("Negative value not allowed:{}".format(ticket))
self.__ticket = ticket
@ticket.deleter
def ticket(self):
raise AttributeError("Can not delete ticket")
可以看到代码增加了不少,但重复的逻辑也出现了不少。虽然property可以让类从外部看起来接口整洁漂亮,但是却做不到内部同样整洁漂亮。
描述符登场
什么是描述符?
一般来说,描述符是一个具有绑定行为的对象属性,其属性的访问被描述符协议方法覆写。这些方法是__get__()、__set__()和__delete__(),一个对象中只要包含了这三个方法中的至少一个就称它为描述符。
描述符有什么作用?
The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting witha.__dict__[‘x’], then type(a).__dict__[‘x’], and continuing through the base classes of type(a) excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods were defined.—–摘自官方文档
简单的说描述符会改变一个属性的基本的获取、设置和删除方式。
先看如何用描述符来解决上面 property逻辑重复的问题。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Integer(object):
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if value < 0:
raise ValueError("Negative value not allowed")
instance.__dict__[self.name] = value
class Movie(object):
score = Integer('score')
ticket = Integer('ticket')
因为描述符优先级高并且会改变默认的get、set行为,这样一来,当我们访问或者设置Movie().score的时候都会受到描述符Integer的限制。
不过我们也总不能用下面这样的方式来创建实例。
a = Movie()
a.score = 1
a.ticket = 2
a.title = ‘test’
a.descript = ‘…’
这样太生硬了,所以我们还缺一个构造函数。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Integer(object):
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if value < 0:
raise ValueError('Negative value not allowed')
instance.__dict__[self.name] = value
class Movie(object):
score = Integer('score')
ticket = Integer('ticket')
def __init__(self, title, description, score, ticket):
self.title = title
self.description = description
self.score = score
self.ticket = ticket
这样在获取、设置和删除score和ticket的时候都会进入Integer的__get__、__set__,从而减少了重复的逻辑。
现在虽然问题得到了解决,但是你可能会好奇这个描述符到底是如何工作的。具体来说,在__init__函数里访问的是自己的self.score和self.ticket,怎么和类属性score和ticket关联起来的?
描述符如何工作
看官方的说明
If an object defines both __get__() and __set__(), it is considered a data descriptor. Descriptors that only define __get__() are called non-data descriptors (they are typically used for methods but other uses are possible).
Data and non-data descriptors differ in how overrides are calculated with respect to entries in an instance’s dictionary. If an instance’s dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence. If an instance’s dictionary has an entry with the same name as a non-data descriptor, the dictionary entry takes precedence.
The important points to remember are:
descriptors are invoked by the __getattribute__() method
overriding __getattribute__() prevents automatic descriptor calls
object.__getattribute__() and type.__getattribute__() make different calls to __get__().
data descriptors always override instance dictionaries.
non-data descriptors may be overridden by instance dictionaries.
类调用__getattribute__()的时候大概是下面这样子:
1
2
3
4
5
6
7
def __getattribute__(self, key):
"Emulate type_getattro() in Objects/typeobject.c"
v = object.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
下面是摘自国外一篇博客上的内容。
Given a Class “C” and an Instance “c” where “c = C(…)”, calling “c.name” means looking up an Attribute “name” on the Instance “c” like this:
Get the Class from Instance
Call the Class’s special method getattribute__. All objects have a default __getattribute
Inside getattribute
Get the Class’s mro as ClassParents
For each ClassParent in ClassParents
If the Attribute is in the ClassParent’s dict
If is a data descriptor
Return the result from calling the data descriptor’s special method __get__()
Break the for each (do not continue searching the same Attribute any further)
If the Attribute is in Instance’s dict
Return the value as it is (even if the value is a data descriptor)
For each ClassParent in ClassParents
If the Attribute is in the ClassParent’s dict
If is a non-data descriptor
Return the result from calling the non-data descriptor’s special method __get__()
If it is NOT a descriptor
Return the value
If Class has the special method getattr
Return the result from calling the Class’s special method__getattr__.
我对上面的理解是,访问一个实例的属性的时候是先遍历它和它的父类,寻找它们的__dict__里是否有同名的data descriptor如果有,就用这个data descriptor代理该属性,如果没有再寻找该实例自身的__dict__,如果有就返回。任然没有再查找它和它父类里的non-data descriptor,最后查找是否有__getattr__
描述符的应用场景
python的property、classmethod修饰器本身也是一个描述符,甚至普通的函数也是描述符(non-data discriptor)
django model和SQLAlchemy里也有描述符的应用
Python
1
2
3
4
5
6
7
8
9
10
11
12
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
后记
只有当确实需要在访问属性的时候完成一些额外的处理任务时,才应该使用property。不然代码反而会变得更加啰嗦,而且这样会让程序变慢很多。
I. 请问python描述符property中的self.fget(instance)怎么理解
self.fget等价于 __init__(self,fget=None,fset=None,fdel=None) 方法中的 fset参数
==================看这下面=================================
class C:
def __init__(self):
self._x = None
def getX(self):
return self._x
def setX(self, value):
self._x = value
def delX(self):
del self._x
x = MyProperty(getX, setX, delX)
执行上面的语句后self.fget就等于类C里的getX
self.fget(instance)等价于调用C里的getX(instance)方法
===========我是新手刚看到这如有兴趣可共同学习================