Python描述符,python文件描述符

  Python描述符,python文件描述符

  描述符是一种在多个属性上重用相同访问逻辑的方法,它可以“劫持”那些原本属于自己的操作。__词典_ _。描述符通常是至少包含__get__ 、__set__、和__delete__方法之一的类,给人一种“将一个类的操作委托给另一个类”的感觉。静态方法、类方法和属性都是构建描述符的类。

  让我们首先看一个简单描述符的例子:

  classMyDescriptor(对象):

  _value=

  def__get__(self,instance,klass):

  回归自我。_值

  def__set__(自身,实例,值):

  自我。_value=value.swapcase()

  classSwap(object):

  Swap=MyDescriptor()注意,MyDescriptor应该使用一个新类。打电话:

  in[1]: from descriptor _ exampleimportSwap

  In[2]:instance=Swap()

  In[3]:instance.swap#未报告AttributeError错误,因为对swap的属性访问被descriptor类重载。

  Out[3]:

  in[4]: instance . swap= make it WAP # use _ _ set _ _重置_value。

  In[5]:instance.swap

  Out[5]:MAKEITSWAP

  在[6]:实例中。__dict__#未使用_ _ dict _ _ 3360被劫持

  Out[6]:{}这就是描述符的力量。如果你不理解众所周知的staticmethod和classmethod,看一下用Python实现的效果可能会更清楚:

  classmyStaticMethod(object):

  .def__init__(self,method):

  .self.staticmethod=方法

  .def__get__(self,object,type=None):

  .returnself.staticmethod

  .

  classmyClassMethod(对象):

  .def__init__(self,method):

  .self.classmethod=方法

  .def__get__(self,object,klass=None):

  .

  nbsp;ifklassisNone:

  ...klass=type(object)

  ...defnewfunc(*args):

  ...returnself.classmethod(klass,*args)

  ...returnnewfunc在实际的生产项目中,描述符有什么用处呢?首先看MongoEngine中的Field的用法:

  

frommongoengineimport*

  classMetadata(EmbeddedDocument):

  tags=ListField(StringField())

  revisions=ListField(IntField())

  classWikiPage(Document):

  title=StringField(required=True)

  text=StringField()

  metadata=EmbeddedDocumentField(Metadata)

有非常多的Field类型,其实它们的基类就是一个描述符,我简化下,大家看看实现的原理:

  

classBaseField(object):

  name=None

  def__init__(self,**kwargs):

  self.__dict__.update(kwargs)

  ...

  def__get__(self,instance,owner):

  returninstance._data.get(self.name)

  def__set__(self,instance,value):

  ...

  instance._data[self.name]=value

很多项目的源代码看起来很复杂,在抽丝剥茧之后,其实原理非常简单,复杂的是业务逻辑。

  接着我们再看Flask的依赖Werkzeug中的cached_property:

  

class_Missing(object):

  def__repr__(self):

  return'novalue'

  def__reduce__(self):

  return'_missing'

  _missing=_Missing()

  classcached_property(property):

  def__init__(self,func,name=None,doc=None):

  self.__name__=nameorfunc.__name__

  self.__module__=func.__module__

  self.__doc__=docorfunc.__doc__

  self.func=func

  def__set__(self,obj,value):

  obj.__dict__[self.__name__]=value

  def__get__(self,obj,type=None):

  ifobjisNone:

  returnself

  value=obj.__dict__.get(self.__name__,_missing)

  ifvalueis_missing:

  value=self.func(obj)

  obj.__dict__[self.__name__]=value

  returnvalue

其实看类的名字就知道这是缓存属性的,看不懂没关系,用一下:

  

classFoo(object):

  @cached_property

  deffoo(self):

  print'Callme!'

  return42

调用下:

  

In[1]:fromcached_propertyimportFoo

  ...:foo=Foo()

  ...:

  In[2]:foo.bar

  Callme!

  Out[2]:42

  In[3]:foo.bar

  Out[3]:42

可以看到在从第二次调用bar方法开始,其实用的是缓存的结果,并没有真的去执行。

  说了这么多描述符的用法。我们写一个做字段验证的描述符:

  

classQuantity(object):

  def__init__(self,name):

  self.name=name

  def__set__(self,instance,value):

  ifvalue>0:

  instance.__dict__[self.name]=value

  else:

  raiseValueError('valuemustbe>0')

  classRectangle(object):

  height=Quantity('height')

  width=Quantity('width')

  def__init__(self,height,width):

  self.height=height

  self.width=width

  @property

  defarea(self):

  returnself.height*self.width

我们试一试:

  

In[1]:fromrectangleimportRectangle

  In[2]:r=Rectangle(10,20)

  In[3]:r.area

  Out[3]:200

  In[4]:r=Rectangle(-1,20)

  ---------------------------------------------------------------------------

  ValueErrorTraceback(mostrecentcalllast)

  <ipython-input-5-5a7fc56e8a>in<module>()

  ---->1r=Rectangle(-1,20)

  /Users/dongweiming/mp/2017-03-23/rectangle.pyin__init__(self,height,width)

  15

  16def__init__(self,height,width):

  --->17self.height=height

  18self.width=width

  19

  /Users/dongweiming/mp/2017-03-23/rectangle.pyin__set__(self,instance,value)

  7instance.__dict__[self.name]=value

  8else:

  ---->9raiseValueError('valuemustbe>0')

  10

  11

  ValueError:valuemustbe>0

看到了吧,我们在描述符的类里面对传值进行了验证。ORM就是这么玩的!

  但是上面的这个实现有个缺点,就是不太自动化,你看height = Quantity('height'),这得让属性和Quantity的name都叫做height,那么可不可以不用指定name呢?当然可以,不过实现的要复杂很多:

  

classQuantity(object):

  __counter=0

  def__init__(self):

  cls=self.__class__

  prefix=cls.__name__

  index=cls.__counter

  self.name='_{}#{}'.format(prefix,index)

  cls.__counter+=1

  def__get__(self,instance,owner):

  ifinstanceisNone:

  returnself

  returngetattr(instance,self.name)

  ...

  classRectangle(object):

  height=Quantity()

  width=Quantity()

  ...

Quantity的name相当于类名+计时器,这个计时器每调用一次就叠加1,用此区分。有一点值得提一提,在__get__中的:

  

ifinstanceisNone:

  returnself

在很多地方可见,比如之前提到的MongoEngine中的BaseField。这是由于直接调用Rectangle.height这样的属性时候会报AttributeError, 因为描述符是实例上的属性。

  PS:这个灵感来自《Fluent Python》,书中还有一个我认为设计非常好的例子。就是当要验证的内容种类很多的时候,如何更好地扩展的问题。现在假设我们除了验证传入的值要大于0,还得验证不能为空和必须是数字(当然三种验证在一个方法中验证也是可以接受的,我这里就是个演示),我们先写一个abc的基类:

  

classValidated(abc.ABC):

  __counter=0

  def__init__(self):

  cls=self.__class__

  prefix=cls.__name__

  index=cls.__counter

  self.name='_{}#{}'.format(prefix,index)

  cls.__counter+=1

  def__get__(self,instance,owner):

  ifinstanceisNone:

  returnself

  else:

  returngetattr(instance,self.name)

  def__set__(self,instance,value):

  value=self.validate(instance,value)

  setattr(instance,self.name,value)

  @abc.abstractmethod

  defvalidate(self,instance,value):

  """returnvalidatedvalueorraiseValueError"""

现在新加一个检查类型,新增一个继承了Validated的、包含检查的validate方法的类就可以了:

  

classQuantity(Validated):

  defvalidate(self,instance,value):

  ifvalue<=0:

  raiseValueError('valuemustbe>0')

  returnvalue

  classNonBlank(Validated):

  defvalidate(self,instance,value):

  value=value.strip()

  iflen(value)==0:

  raiseValueError('valuecannotbeemptyorblank')

  returnvalue

前面展示的描述符都是一个类,那么可不可以用函数来实现呢?也是可以的:

  

defquantity():

  try:

  quantity.counter+=1

  exceptAttributeError:

  quantity.counter=0

  storage_name='_{}:{}'.format('quantity',quantity.counter)

  defqty_getter(instance):

  returngetattr(instance,storage_name)

  defqty_setter(instance,value):

  ifvalue>0:

  setattr(instance,storage_name,value)

  else:

  raiseValueError('valuemustbe>0')

  returnproperty(qty_getter,qty_setter)

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • python文件路径操作方法总结怎么写,python文件路径操作方法总结表,python文件路径操作方法总结
  • python文件路径操作方法总结怎么写,python文件路径操作方法总结图
  • ,,PyInstaller将Python文件打包为exe后如何反编译(破解源码)以及防止反编译
  • python文件加密存储,python通过文件读写,复制图片
  • python文件打开的默认方式是只读,python以只读方式打开文本文件的参数
  • python文件读写,python将数据写入文件和从文件读取
  • 怎么把python文件变成可执行exe,python将py文件编译成exe文件
  • 打开python文件的两种方式,python文件是怎么打开
  • python文件操作write,python文件write函数的用法
  • linux如何编译python文件,linux上python编译器
  • python打不开文件怎么回事,做好的python文件直接打开怎么不能用
  • python中绝对路径,python文件相对路径怎么写
  • python对文件的操作方法有哪些,python文件的基本操作
  • python关于文件的题目,python文件概述
  • python文件可以直接运行吗,python直接运行
  • 留言与评论(共有 条评论)
       
    验证码: