亚洲精品中文字幕无乱码_久久亚洲精品无码AV大片_最新国产免费Av网址_国产精品3级片

網頁設計

Python中的類與對象之描述符

時間:2024-05-17 18:33:08 美云 網頁設計 我要投稿
  • 相關推薦

Python中的類與對象之描述符

  描述符(descriptors)是python語言中一個深奧但卻重要的一部分。它們廣泛應用于python語言的內核,熟練掌握描述符將會為python程序員的工具箱添加一個額外的技巧。下面是小編為大家整理的Python中的類與對象之描述符,歡迎大家借鑒與參考,希望對大家有所幫助。

  1、假設一個程序中,我們需要對一個對象屬性執(zhí)行嚴格的類型檢查。然而,Python是一種動態(tài)語言,所以并不支持類型檢查,但是這并不妨礙我們實現(xiàn)自己版本,且較為初級的類型檢查。對象屬性類型檢查的傳統(tǒng)方法可能采用下面的方式:

  def __init__(self, name, age):

  if isinstance(str, name):

  self.name = name

  else:

  raise TypeError("Must be a string")

  if isinstance(int, age):

  self.age = age

  else:

  raise TypeError("Must be an int")

  上面是執(zhí)行這種類型檢查的一種方法,但是參數(shù)數(shù)量增加時它將變得比較繁瑣。另外,在賦值之前,我們可以創(chuàng)建一個在__init__中調用的type_check(type, val)函數(shù),但是當我們想在其他地方設置屬性值時,該如何簡單地實現(xiàn)這種檢查呢。我想到的一個快速解決方案是Java中的getters和setters,但是這并不符合Python風格,并且比較麻煩。

  2、假設在一個程序中,我們想創(chuàng)建一些在運行時立刻初始化然后變成只讀的屬性。有人也能想到利用Python中的特殊方法來實現(xiàn),但這種實現(xiàn)方法仍舊是笨拙和繁瑣的。

  3、最后,設想一個程序中,我們希望以某種方式自定義對象屬性的訪問。例如需要記錄這種屬性的訪問。同樣的,還是可以想到一個解決方法,即使這種解決方案可能比較笨重并且不可復用。

  上述問題因都與屬性引用相關而全部聯(lián)系在了一起。下面,我們將嘗試自定義屬性的訪問方法。

  Python描述符

  針對上面所列的問題,描述符提供了優(yōu)雅、簡潔、健壯和可重用的解決方案。簡而言之,一個描述符就是一個對象,該對象代表了一個屬性的值。這就意味著如果一個賬戶對象有一個屬性“name”,那么描述符就是另一個能夠用來代表屬性“name”持有值的對象。描述符協(xié)議中“定義了__get__”、“__set__”或”____” 這些特殊方法,描述符是實現(xiàn)其中一個或多個方法的對象。這些方法中每一種方法的簽名如下所示:

  python descr.get(self,obj,type=None)->value。

  descr.__set__(self, obj, value) --> None

  descr.____(self, obj) --> None

  實現(xiàn)__get__方法的對象是非數(shù)據(jù)描述符,意味著在初始化之后它們只能被讀取。而同時實現(xiàn)__get__和__set__的對象是數(shù)據(jù)描述符,意味著這種屬性是可寫的。

  為了更好地理解描述符,我們給出針對上述問題基于描述符的解決方法。使用Python描述符實現(xiàn)對象屬性的類型檢查將是一個非常簡單的任務。裝飾器實現(xiàn)這種類型檢查的代碼如下所示:

  class TypedProperty(object):

  def __init__(self, name, type, default=None):

  self.name = "_" + name

  self.type = type

  self.default = default if default else type()

  def __get__(self, instance, cls):

  return getattr(instance, self.name, self.default)

  def __set__(self,instance,value):

  if not isinstance(value,self.type):

  raise TypeError("Must be a %s" % self.type)

  setattr(instance,self.name,value)

  def ____(self,instance):

  raise AttributeError("Cant attribute")

  class Foo(object):

  name = TypedProperty("name",str)

  num = TypedProperty("num",int,42)

  >> acct = Foo()

  >> acct.name = "obi"

  >> acct.num = 1234

  >> print acct.num

  1234

  >> print acct.name

  obi

  # trying to assign a string to number fails

  >> acct.num = 1234

  TypeError: Must be a

  在這個例子中,我們實現(xiàn)了一個描述符TypedProperty,并且這個描述符類會對它所代表的類的任何屬性執(zhí)行類型檢查。注意到這一點很重要,即描述符只能在類級別進行合法定義,而不能在實例級別定義。例如,在上面例子中的__init__方法里。

  當訪問類Foo實例的任何屬性時,描述符會調用它的__get__方法。需要注意的是,__get__方法的第一個參數(shù)是描述符代表的屬性被引用的源對象。當屬性被分配時,描述符會調用它的__set__方法。為了理解為什么可以使用描述符代表對象屬性,我們需要理解Python中屬性引用解析的執(zhí)行方式。對于對象來說,屬性解析機制在object.__getattribute__()中。該方法將b.x轉換成type(b).__dict__[x].__get__(b, type(b))。然后,解析機制使用優(yōu)先級鏈搜索屬性,在優(yōu)先級鏈中,類字典中發(fā)現(xiàn)的數(shù)據(jù)描述符的優(yōu)先級高于實例變量,實例變量優(yōu)先級高于非數(shù)據(jù)描述符,如果提供了getattr(),優(yōu)先級鏈會為getattr()分配最低優(yōu)先級。對于一個給定的對象類,可以通過自定義__getattribute__方法來重寫優(yōu)先級鏈。

  深刻理解優(yōu)先級鏈之后,就很容易想出針對前面提出的第二個和第三個問題的優(yōu)雅解決方案了。那就是,利用描述符實現(xiàn)一個只讀屬性將變成實現(xiàn)數(shù)據(jù)描述符這個簡單的情況了,即不帶__set__方法的描述符。盡管在本例中不重要,定義訪問方式的問題只需要在__get__和__set__方法中增加所需的功能即可。

  類屬性

  每次我們想使用描述符的時候都不得不定義描述符類,這樣看起來非常繁瑣。Python特性提供了一種簡潔的方式用來向屬性增加數(shù)據(jù)描述符。一個屬性簽名如下所示:

  property(fget=None, fset=None, fdel=None, doc=None) -> property attribute

  fget、fset和fdel分別是類的getter、setter和r方法。我們通過下面的一個示例來說明如何創(chuàng)建屬性:

  class Accout(object):

  def __init__(self):

  self._acct_num = None

  def get_acct_num(self):

  return self._acct_num

  def set_acct_num(self, value):

  self._acct_num = value

  def del_acct_num(self):

  del self._acct_num

  acct_num = property(get_acct_num, set_acct_num, del_acct_num, "Account number property.")

  如果acct是Account的一個實例,acct.acct_num將會調用getter,acct.acct_num = value將調用setter,del acct_num.acct_num將調用r。

  在Python中,屬性對象和功能可以像《描述符指南》中說明的那樣使用描述符協(xié)議來實現(xiàn),如下所示:

  class Property(object):

  "Emulate PyProperty_Type() in Objects/descrobject.c"

  def __init__(self, fget=None, fset=None, fdel=None, doc=None):

  self.fget = fget

  self.fset = fset

  self.fdel = fdel

  if doc is None and fget is not None:

  doc = fget.__doc__

  self.__doc__ = doc

  def __get__(self, obj, objtype=None):

  if obj is None:

  return self

  if self.fget is None:

  raise AttributeError("unreadable attribute")

  return self.fget(obj)

  def __set__(self, obj, value):

  if self.fset is None:

  raise AttributeError("cant set attribute")

  self.fset(obj, value)

  def ____(self, obj):

  if self.fdel is None:

  raise AttributeError("cant attribute")

  self.fdel(obj)

  def getter(self, fget):

  return type(self)(fget, self.fset, self.fdel, self.__doc__)

  def setter(self, fset):

  return type(self)(self.fget, fset, self.fdel, self.__doc__)

  def r(self, fdel):

  return type(self)(self.fget, self.fset, fdel, self.__doc__)

  Python也提供了@ property裝飾器,可以用它來創(chuàng)建只讀屬性。一個屬性對象擁有getter、setter和r裝飾器方法,可以使用它們通過對應的被裝飾函數(shù)的accessor函數(shù)創(chuàng)建屬性的拷貝。下面的例子最好地解釋了這一點:

  class C(object):

  def __init__(self):

  self._x = None

  @property

  # the x property. the decorator creates a read-only property

  def x(self):

  return self._x

  @x.setter

  # the x property setter makes the property writeable

  def x(self, value):

  self._x = value

  @x.r

  def x(self):

  del self._x

  如果我們想讓屬性只讀,那么我們可以去掉setter方法。

  在Python語言中,描述符有著廣泛的應用。Python函數(shù)、類方法、靜態(tài)方法都是非數(shù)據(jù)描述符的例子。針對列舉的Python對象是如何使用描述符實現(xiàn)的問題,《描述符指南》給出了一個基本的描述。

  定義

  這些方法有__get__(),__set__(),____.如果這些方法中任何一個被定義在一個對象中,這個對象就是一個描述符

  屬性的優(yōu)先級

  1.__getattribute__(), 無條件調用 #

  2.數(shù)據(jù)描述符,在訪問成員屬性的時候觸發(fā)調用 (若人為的重載了該 __getattribute__() 方法,可能會調職無法調用描述符)

  3.實例對象的字典(若與描述符對象同名,會被覆蓋哦)

  4.類的字典

  5.非數(shù)據(jù)描述符

  6.父類的字典

  7.__getattr__() 方法

【Python中的類與對象之描述符】相關文章:

理解java和python類變量04-02

簡明的 Python 編程規(guī)范12-03

廈門2014年中考新增兩類加分對象03-24

PHP編程:類和對象、方法調用11-27

Java中對象和引用的具體區(qū)別12-04

php學習筆記:類的聲明與對象實例化11-16

Python中下劃線的方法11-19

利用python實現(xiàn)簡單爬蟲功能03-19

PHP面向對象程序設計之接口用法03-02

使用python實現(xiàn)Linux異步epoll的代碼03-01