摘要:項目地址本篇主要關于三個常用內置方法,,在語言的設計中,通常的語法操作最終都會轉化為方法調用,例如相當于中的描述符就是將對象屬性的獲取賦值以及刪除等行為轉換為方法調用的協議例如我們要獲取一個對象的屬性,可以通過的方式取得而通過的
項目地址:https://git.io/pytips
本篇主要關于三個常用內置方法:property(),staticmethod(),classmethod()
在 Python 語言的設計中,通常的語法操作最終都會轉化為方法調用,例如:
a = 1 b = 2 print("a + b = {}".format(a+b)) # 相當于 print("a.__add__(b) = {}".format(a.__add__(b)))
a + b = 3 a.__add__(b) = 3
Python 中的描述符(Descriptor)就是將對象屬性的獲取、賦值以及刪除等行為轉換為方法調用的協議:
descr.__get__(self, obj, type=None) --> value descr.__set__(self, obj, value) --> None descr.__delete__(self, obj) --> None
例如我們要獲取一個對象的屬性,可以通過o.x的方式取得:
class Int: ctype = "Class::Int" def __init__(self, val): self._val = val a = Int(1) print(a.ctype)
Class::Int
而通過.的方式尋找屬性的值實際上調用了object.__getattribute__(self, name)方法:
class Int: ctype = "Class::Int" def __init__(self, val): self._val = val def __getattribute__(self, name): print("? doesn"t want to give `{}" to you!".format(name)) return "?" a = Int(2) print(a.ctype)
? doesn"t want to give `ctype" to you! ?
而這里的__getattribute__(self, name)方法實際上就是將.的屬性獲取方法轉化為描述符協議定義的descr.__get__(self, key):
class Str: def __init__(self, val): self._val = val def __get__(self, name, ctype=None): print("You can __get__ anything from here!") return self._val class Int: ctype = Str("Class::Int") def __init__(self, val): self._val = val def __getattribute__(self, name): return type(self).__dict__[name].__get__(None, type(self)) a = Int(2) print(a.ctype)
You can __get__ anything from here! Class::Int
這里的 a.ctype = (Int.__dict__["ctype"]).__get__(None, Int),即通過描述符的方式獲取了 ctype 屬性的值。同樣的道理,你也可以通過 descr.__set__(self, obj, val) 設置屬性的值:
class Str: def __init__(self, val): self._val = val def __get__(self, name, ctype=None): print("You can __get__ anything from here!") return self._val def __set__(self, name, val): print("You can __set__ anything to me!") self._val = val class Int: ctype = Str("Class::Int") def __init__(self, val): self._val = val a = Int(3) print(a.ctype) a.ctype = "Class::Float" print(a.ctype)
You can __get__ anything from here! Class::Int You can __set__ anything to me! You can __get__ anything from here! Class::Float
將這些取值、賦值的操作轉換為方法調用讓我們有辦法在做這些操作的過程中插入一些小動作,這么好用的東西自然是已加入豪華內置函數陣容,正是我們常見的
property()
classmethod()
staticmethod()
property
property(fget=None, fset=None, fdel=None, doc=None) 方法簡化了上面的操作:
class Int: def __init__(self, val): self._val = val self._ctype = None def get_ctype(self): print("INFO: You can get `ctype`") return self._ctype def set_ctype(self, val): print("INFO: You"re setting `ctype` =", val) self._ctype=val ctype = property(fget=get_ctype, fset=set_ctype, doc="Property `ctype`") a = Int(4) print(a.ctype) a.ctype = "Class::Int" print(a.ctype)
INFO: You can get `ctype` None INFO: You"re setting `ctype` = Class::Int INFO: You can get `ctype` Class::Int
顯然,更方便一些的用法是將 property 當做修飾器:
class Int: _ctype = None def __init__(self, val): self._val = val @property def ctype(self): print("INFO: You can get `ctype` from me!") return self._ctype @ctype.setter def ctype(self, val): print("INFO: You"re setting `ctype` =", val) self._ctype = val a = Int(5) print(a.ctype) a.ctype = "Class::Int" print(a.ctype)
INFO: You can get `ctype` from me! None INFO: You"re setting `ctype` = Class::Int INFO: You can get `ctype` from me! Class::Int
staticmethod & classmethod
顧名思義,property 是關于屬性的全部操作,如果是要獲取類中的方法,則需要用到 staticmethod 和 classmethod。顧名思義,staticmethod 將方法變成靜態方法,即類和實例都可以訪問,如果不用 staticmethod 我們可以用下面這種別扭的方法實現:
class Int: def __init__(self, val): self._val = val def _get_ctype(self=None): print("INFO: You can get `ctype` from here!") return "Class::Int" @staticmethod def get_ctype(): print("INFO: You can get `ctype` from here!") return "Class::StaticInt" a = Int(6) print(a._get_ctype()) print(Int._get_ctype()) print(a.get_ctype()) print(Int.get_ctype())
INFO: You can get `ctype` from here! Class::Int INFO: You can get `ctype` from here! Class::Int INFO: You can get `ctype` from here! Class::StaticInt INFO: You can get `ctype` from here! Class::StaticInt
可以看到,靜態方法與類和實例無關,也就不再(不能)需要 self 關鍵詞;與之相反,當我們需要在方法中保留類(而非實例)的引用時,則需要用 classmethod:
class Int: _ctype = "" def __init__(self, val): self._val = val @classmethod def set_ctype(klass, t): klass._ctype = t return "{}.ctype = {}".format(klass.__name__, t) a = Int(7) print(a.set_ctype("Class::Int")) print(Int.set_ctype("Class::Float")) b = Int(8) print(b._ctype)
Int.ctype = Class::Int Int.ctype = Class::Float Class::Float總結
Python 的描述符給出一種通過方法調用來實現屬性(方法)獲取、賦值等操作的規則,通過這一規則可以方便我們深入程序內部并實施操控,因此 property/staticmethod/classmethod 在 Python 是通過底層(如 CPython 中的 C)實現的,如果想要進一步深入了解其實現原理,可以訪問參考鏈接的教程,其中包括了這三個內置方法的 Python 實現版本,我也把它們 copy 過來方便查看。
歡迎關注公眾號 PyHub 每日推送
參考Descriptor HowTo Guide
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("can"t set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None: raise AttributeError("can"t delete 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 deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__) class StaticMethod(object): "Emulate PyStaticMethod_Type() in Objects/funcobject.c" def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f class ClassMethod(object): "Emulate PyClassMethod_Type() in Objects/funcobject.c" def __init__(self, f): self.f = f def __get__(self, obj, klass=None): if klass is None: klass = type(obj) def newfunc(*args): return self.f(klass, *args) return newfunc
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/45427.html
摘要:項目地址中的函數式編程函數式編程英語或稱函數程序設計,又稱泛函編程,是一種編程范型,它將電腦運算視為數學上的函數計算,并且避免使用程序狀態以及易變對象。 項目地址:https://git.io/pytips Python 中的函數式編程 函數式編程(英語:functional programming)或稱函數程序設計,又稱泛函編程,是一種編程范型,它將電腦運算視為數學上的函數計算,并且...
摘要:模塊的導入一定要放在最上方,也就是在所有其它模塊之前導入。最后一列是每個新特性所對應的及簡單描述。相對導入則可以使用為標記導入相對目錄中的模塊,具體可以參考這篇文章導入模塊的幾種姿勢。 項目地址:https://git.io/pytips 我們經常從一些組織良好的 Python 項目中看到 __future__ 的身影,例如: from __future__ import absolu...
摘要:回到對字節和字節數組的定義為了用計算機可以理解的數字描述人類使用的字符,我們需要一張數字與字符對應的表。由于和字符串一樣是序列類型,字節和字節數組可用的方法也類似,這里就不一一列舉了。 項目地址:https://git.io/pytips 0x07 中介紹了 Python 中的字符串類型,字符串類型是對人類友好的符號,但計算機只認識一種符號,那就是二進制(binary)數,或者說是數字...
摘要:項目地址相信很多人在格式化字符串的時候都用的語法,提出一種更先進的格式化方法并成為的標準用來替換舊的格式化語法,從開始已經實現了這一方法其它解釋器未考證。 項目地址:https://git.io/pytips 相信很多人在格式化字符串的時候都用%s % v的語法,PEP 3101 提出一種更先進的格式化方法 str.format() 并成為 Python 3 的標準用來替換舊的 %s ...
摘要:中關于線程的標準庫是,之前在版本中的在之后更名為,無論是還是都應該盡量避免使用較為底層的而應該使用。而與線程相比,協程尤其是結合事件循環無論在編程模型還是語法上,看起來都是非常友好的單線程同步過程。 項目地址:https://git.io/pytips 要說到線程(Thread)與協程(Coroutine)似乎總是需要從并行(Parallelism)與并發(Concurrency)談起...
閱讀 3548·2021-08-31 09:39
閱讀 1854·2019-08-30 13:14
閱讀 2919·2019-08-30 13:02
閱讀 2769·2019-08-29 13:22
閱讀 2341·2019-08-26 13:54
閱讀 767·2019-08-26 13:45
閱讀 1586·2019-08-26 11:00
閱讀 982·2019-08-26 10:58