Introduction
Descriptors (descriptors) are a profound but important black magic in the Python language. They are widely used in the kernel of the Python language. Proficiency in descriptors will benefit Python programmers. Toolbox adds an extra trick. In this article, I will describe the definition of descriptors and some common scenarios, and at the end of the article I will add __getattr__, __getattribute__, and __getitem__, three magic methods that also involve attribute access.
Definition of descriptor
descr__get__(self,?obj,?objtype=None)?-->?value descr.__set__(self,?obj,?value)?-->?None descr.__delete__(self,?obj)?-->?None
As long as an object attribute (object attribute) defines any one of the above three methods, then this class can be called a descriptor class.
Descriptor Basics
In the following example we create a RevealAcess class and implement the __get__ method. Now this class can be called a descriptor class.
class?RevealAccess(object): ????def?__get__(self,?obj,?objtype): ????????print('self?in?RevealAccess:?{}'.format(self)) ????????print('self:?{}\nobj:?{}\nobjtype:?{}'.format(self,?obj,?objtype)) class?MyClass(object): ????x?=?RevealAccess() ????def?test(self): ????????print('self?in?MyClass:?{}'.format(self))
EX1 instance attribute
Next let’s take a look at the meaning of each parameter of the __get__ method. In the following example, self is the instance x of the RevealAccess class, and obj is the MyClass class. Instance m, objtype, as the name suggests, is the MyClass class itself. As can be seen from the output statement, m.x access descriptor x will call the __get__ method.
>>>?m?=?MyClass() >>>?m.test() self?in?MyClass:?<__main__.MyClass object at 0x7f19d4e42160> >>>?m.x self?in?RevealAccess:?<__main__.RevealAccess object at 0x7f19d4e420f0> self:?<__main__.RevealAccess object at 0x7f19d4e420f0> obj:?<__main__.MyClass object at 0x7f19d4e42160> objtype:?<class '__main__.MyClass'>
EX2 class attribute
If you access attribute x directly through the class, then the obj connection is directly None, which is easier to understand because there is no instance of MyClass.
>>>?MyClass.x self?in?RevealAccess:?<__main__.RevealAccess object at 0x7f53651070f0> self:?<__main__.RevealAccess object at 0x7f53651070f0> obj:?None objtype:?<class '__main__.MyClass'>
The principle of descriptor
Descriptor trigger
In the above example, we enumerated the usage of descriptors from the perspective of instance attributes and class attributes. Below we Let’s carefully analyze the internal principle:
If you access instance attributes, it is equivalent to calling object.__getattribute__(), which translates obj.d into type(obj ).__dict__['d'].__get__(obj, type(obj)).
If you are accessing a class attribute, it is equivalent to calling type.__getattribute__(), which translates cls.d into cls.__dict__['d'].__get__( None, cls), converted into Python code is:
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
Let’s briefly talk about the __getattribute__ magic method. This method will be called unconditionally when we access the attributes of an object. Details I will make an additional supplement at the end of the article about the details such as the difference between __getattr and __getitem__, but we will not delve into it for now.
Descriptor priority
First of all, descriptors are divided into two types:
If an object defines both __get__() and __set__ () method, this descriptor is called a data descriptor.
If an object only defines the __get__() method, this descriptor is called a non-data descriptor.
There are four situations when we access properties:
data descriptor
instance dict
non-data descriptor
__getattr__()
Their priority The size is:
data?descriptor?>?instance?dict?>?non-data?descriptor?>?__getattr__()
What does this mean? That is to say, if data descriptor->d and instance attribute->d with the same name appear in the instance object obj, when obj.d accesses attribute d, Python will call it because the data descriptor has a higher priority. type(obj).__dict__['d'].__get__(obj, type(obj)) instead of calling obj.__dict__['d']. But if the descriptor is a non-data descriptor, Python will call obj.__dict__['d'].
Property
Defining a descriptor class every time a descriptor is used seems very cumbersome. Python provides a concise way to add data descriptors to properties.
property(fget=None,?fset=None,?fdel=None,?doc=None)?->?property?attribute
fget, fset and fdel are the getter, setter and deleter methods of the class respectively. We use the following example to illustrate how to use Property:
class?Account(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,?'_acct_num?property.')
If acct is an instance of Account, acct.acct_num will call the getter, acct.acct_num = value will call the setter, and del acct_num.acct_num will call deleter.
>>>?acct?=?Account() >>>?acct.acct_num?=?1000 >>>?acct.acct_num 1000
Python also provides the @property decorator, which can be used to create properties for simple application scenarios. A property object has getter, setter and delete decorator methods, which can be used to create a copy of the property through the accessor function of the corresponding decorated function.
class?Account(object): ????def?__init__(self): ????????self._acct_num?=?None ????@property ?????#?the?_acct_num?property.?the?decorator?creates?a?read-only?property ????def?acct_num(self): ????????return?self._acct_num ????@acct_num.setter ????#?the?_acct_num?property?setter?makes?the?property?writeable ????def?set_acct_num(self,?value): ????????self._acct_num?=?value ????@acct_num.deleter ????def?del_acct_num(self): ????????del?self._acct_num
If you want the property to be read-only, just remove the setter method.
Create descriptors at runtime
We can add property attributes at runtime:
class?Person(object): ????def?addProperty(self,?attribute): ????????#?create?local?setter?and?getter?with?a?particular?attribute?name ????????getter?=?lambda?self:?self._getProperty(attribute) ????????setter?=?lambda?self,?value:?self._setProperty(attribute,?value) ????????#?construct?property?attribute?and?add?it?to?the?class ????????setattr(self.__class__,?attribute,?property(fget=getter,?\ ????????????????????????????????????????????????????fset=setter,?\ ????????????????????????????????????????????????????doc="Auto-generated?method")) ????def?_setProperty(self,?attribute,?value): ????????print("Setting:?{}?=?{}".format(attribute,?value)) ????????setattr(self,?'_'?+?attribute,?value.title()) ????def?_getProperty(self,?attribute): ????????print("Getting:?{}".format(attribute)) ????????return?getattr(self,?'_'?+?attribute)
>>>?user?=?Person() >>>?user.addProperty('name') >>>?user.addProperty('phone') >>>?user.name?=?'john?smith' Setting:?name?=?john?smith >>>?user.phone?=?'12345' Setting:?phone?=?12345 >>>?user.name Getting:?name 'John?Smith' >>>?user.__dict__ {'_phone':?'12345',?'_name':?'John?Smith'}
Static methods and class methods
We can use descriptors To simulate the implementation of @staticmethod and @classmethod in Python. Let’s first browse the following table:
Called from an Object | Called from a Class | |
---|---|---|
f(obj, *args) | f(*args) | |
f(*args) | f(*args) | |
f(type(obj), *args) | f(klass, *args) |
Transformation | Called from an Object | Called from a Class |
---|---|---|
function | f(obj, *args) | f(*args) |
staticmethod | f(*args) | f(*args) |
classmethod | f(type(obj), *args) | f(klass, *args) |
靜態(tài)方法
對(duì)于靜態(tài)方法f。c.f和C.f是等價(jià)的,都是直接查詢object.__getattribute__(c, ‘f’)或者object.__getattribute__(C, ’f‘)。靜態(tài)方法一個(gè)明顯的特征就是沒(méi)有self變量。
靜態(tài)方法有什么用呢?假設(shè)有一個(gè)處理專(zhuān)門(mén)數(shù)據(jù)的容器類(lèi),它提供了一些方法來(lái)求平均數(shù),中位數(shù)等統(tǒng)計(jì)數(shù)據(jù)方式,這些方法都是要依賴(lài)于相應(yīng)的數(shù)據(jù)的。但是類(lèi)中可能還有一些方法,并不依賴(lài)這些數(shù)據(jù),這個(gè)時(shí)候我們可以將這些方法聲明為靜態(tài)方法,同時(shí)這也可以提高代碼的可讀性。
使用非數(shù)據(jù)描述符來(lái)模擬一下靜態(tài)方法的實(shí)現(xiàn):
class?StaticMethod(object): ????def?__init__(self,?f): ????????self.f?=?f ????def?__get__(self,?obj,?objtype=None): ????????return?self.f
我們來(lái)應(yīng)用一下:
class?MyClass(object): ????@StaticMethod ????def?get_x(x): ????????return?x print(MyClass.get_x(100))??#?output:?100
類(lèi)方法
Python的@classmethod和@staticmethod的用法有些類(lèi)似,但是還是有些不同,當(dāng)某些方法只需要得到類(lèi)的引用而不關(guān)心類(lèi)中的相應(yīng)的數(shù)據(jù)的時(shí)候就需要使用classmethod了。
使用非數(shù)據(jù)描述符來(lái)模擬一下類(lèi)方法的實(shí)現(xiàn):
class?ClassMethod(object): ????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
其他的魔術(shù)方法
首次接觸Python魔術(shù)方法的時(shí)候,我也被__get__, __getattribute__, __getattr__, __getitem__之間的區(qū)別困擾到了,它們都是和屬性訪問(wèn)相關(guān)的魔術(shù)方法,其中重寫(xiě)__getattr__,__getitem__來(lái)構(gòu)造一個(gè)自己的集合類(lèi)非常的常用,下面我們就通過(guò)一些例子來(lái)看一下它們的應(yīng)用。
__getattr__
Python默認(rèn)訪問(wèn)類(lèi)/實(shí)例的某個(gè)屬性都是通過(guò)__getattribute__來(lái)調(diào)用的,__getattribute__會(huì)被無(wú)條件調(diào)用,沒(méi)有找到的話就會(huì)調(diào)用__getattr__。如果我們要定制某個(gè)類(lèi),通常情況下我們不應(yīng)該重寫(xiě)__getattribute__,而是應(yīng)該重寫(xiě)__getattr__,很少看見(jiàn)重寫(xiě)__getattribute__的情況。
從下面的輸出可以看出,當(dāng)一個(gè)屬性通過(guò)__getattribute__無(wú)法找到的時(shí)候會(huì)調(diào)用__getattr__。
In?[1]:?class?Test(object): ????...:?????def?__getattribute__(self,?item): ????...:?????????print('call?__getattribute__') ????...:?????????return?super(Test,?self).__getattribute__(item) ????...:?????def?__getattr__(self,?item): ????...:?????????return?'call?__getattr__' ????...: In?[2]:?Test().a call?__getattribute__ Out[2]:?'call?__getattr__'
應(yīng)用
對(duì)于默認(rèn)的字典,Python只支持以obj['foo']形式來(lái)訪問(wèn),不支持obj.foo的形式,我們可以通過(guò)重寫(xiě)__getattr__讓字典也支持obj['foo']的訪問(wèn)形式,這是一個(gè)非常經(jīng)典常用的用法:
class?Storage(dict): ????""" ????A?Storage?object?is?like?a?dictionary?except?`obj.foo`?can?be?used ????in?addition?to?`obj['foo']`. ????""" ????def?__getattr__(self,?key): ????????try: ????????????return?self[key] ????????except?KeyError?as?k: ????????????raise?AttributeError(k) ????def?__setattr__(self,?key,?value): ????????self[key]?=?value ????def?__delattr__(self,?key): ????????try: ????????????del?self[key] ????????except?KeyError?as?k: ????????????raise?AttributeError(k) ????def?__repr__(self): ????????return?'<Storage ' + dict.__repr__(self) + '>'
我們來(lái)使用一下我們自定義的加強(qiáng)版字典:
>>>?s?=?Storage(a=1) >>>?s['a'] 1 >>>?s.a 1 >>>?s.a?=?2 >>>?s['a'] 2 >>>?del?s.a >>>?s.a ... AttributeError:?'a'
__getitem__
getitem用于通過(guò)下標(biāo)[]的形式來(lái)獲取對(duì)象中的元素,下面我們通過(guò)重寫(xiě)__getitem__來(lái)實(shí)現(xiàn)一個(gè)自己的list。
class?MyList(object): ????def?__init__(self,?*args): ????????self.numbers?=?args ????def?__getitem__(self,?item): ????????return?self.numbers[item] my_list?=?MyList(1,?2,?3,?4,?6,?5,?3) print?my_list[2]
這個(gè)實(shí)現(xiàn)非常的簡(jiǎn)陋,不支持slice和step等功能,請(qǐng)讀者自行改進(jìn),這里我就不重復(fù)了。
應(yīng)用
下面是參考requests庫(kù)中對(duì)于__getitem__的一個(gè)使用,我們定制了一個(gè)忽略屬性大小寫(xiě)的字典類(lèi)。
程序有些復(fù)雜,我稍微解釋一下:由于這里比較簡(jiǎn)單,沒(méi)有使用描述符的需求,所以使用了@property裝飾器來(lái)代替,lower_keys的功能是將實(shí)例字典中的鍵全部轉(zhuǎn)換成小寫(xiě)并且存儲(chǔ)在字典self._lower_keys中。重寫(xiě)了__getitem__方法,以后我們?cè)L問(wèn)某個(gè)屬性首先會(huì)將鍵轉(zhuǎn)換為小寫(xiě)的方式,然后并不會(huì)直接訪問(wèn)實(shí)例字典,而是會(huì)訪問(wèn)字典self._lower_keys去查找。賦值/刪除操作的時(shí)候由于實(shí)例字典會(huì)進(jìn)行變更,為了保持self._lower_keys和實(shí)例字典同步,首先清除self._lower_keys的內(nèi)容,以后我們重新查找鍵的時(shí)候再調(diào)用__getitem__的時(shí)候會(huì)重新新建一個(gè)self._lower_keys。
class?CaseInsensitiveDict(dict): ????@property ????def?lower_keys(self): ????????if?not?hasattr(self,?'_lower_keys')?or?not?self._lower_keys: ????????????self._lower_keys?=?dict((k.lower(),?k)?for?k?in?self.keys()) ????????return?self._lower_keys ????def?_clear_lower_keys(self): ????????if?hasattr(self,?'_lower_keys'): ????????????self._lower_keys.clear() ????def?__contains__(self,?key): ????????return?key.lower()?in?self.lower_keys ????def?__getitem__(self,?key): ????????if?key?in?self: ????????????return?dict.__getitem__(self,?self.lower_keys[key.lower()]) ????def?__setitem__(self,?key,?value): ????????dict.__setitem__(self,?key,?value) ????????self._clear_lower_keys() ????def?__delitem__(self,?key): ????????dict.__delitem__(self,?key) ????????self._lower_keys.clear() ????def?get(self,?key,?default=None): ????????if?key?in?self: ????????????return?self[key] ????????else: ????????????return?default
我們來(lái)調(diào)用一下這個(gè)類(lèi):
>>>?d?=?CaseInsensitiveDict() >>>?d['ziwenxie']?=?'ziwenxie' >>>?d['ZiWenXie']?=?'ZiWenXie' >>>?print(d) {'ZiWenXie':?'ziwenxie',?'ziwenxie':?'ziwenxie'} >>>?print(d['ziwenxie']) ziwenxie #?d['ZiWenXie']?=>?d['ziwenxie'] >>>?print(d['ZiWenXie']) ziwenxie
更多Python黑魔法之描述符相關(guān)文章請(qǐng)關(guān)注PHP中文網(wǎng)!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

To realize text error correction and syntax optimization with AI, you need to follow the following steps: 1. Select a suitable AI model or API, such as Baidu, Tencent API or open source NLP library; 2. Call the API through PHP's curl or Guzzle and process the return results; 3. Display error correction information in the application and allow users to choose whether to adopt it; 4. Use php-l and PHP_CodeSniffer for syntax detection and code optimization; 5. Continuously collect feedback and update the model or rules to improve the effect. When choosing AIAPI, focus on evaluating accuracy, response speed, price and support for PHP. Code optimization should follow PSR specifications, use cache reasonably, avoid circular queries, review code regularly, and use X

User voice input is captured and sent to the PHP backend through the MediaRecorder API of the front-end JavaScript; 2. PHP saves the audio as a temporary file and calls STTAPI (such as Google or Baidu voice recognition) to convert it into text; 3. PHP sends the text to an AI service (such as OpenAIGPT) to obtain intelligent reply; 4. PHP then calls TTSAPI (such as Baidu or Google voice synthesis) to convert the reply to a voice file; 5. PHP streams the voice file back to the front-end to play, completing interaction. The entire process is dominated by PHP to ensure seamless connection between all links.

This article has selected several top Python "finished" project websites and high-level "blockbuster" learning resource portals for you. Whether you are looking for development inspiration, observing and learning master-level source code, or systematically improving your practical capabilities, these platforms are not to be missed and can help you grow into a Python master quickly.

To collect user behavior data, you need to record browsing, search, purchase and other information into the database through PHP, and clean and analyze it to explore interest preferences; 2. The selection of recommendation algorithms should be determined based on data characteristics: based on content, collaborative filtering, rules or mixed recommendations; 3. Collaborative filtering can be implemented in PHP to calculate user cosine similarity, select K nearest neighbors, weighted prediction scores and recommend high-scoring products; 4. Performance evaluation uses accuracy, recall, F1 value and CTR, conversion rate and verify the effect through A/B tests; 5. Cold start problems can be alleviated through product attributes, user registration information, popular recommendations and expert evaluations; 6. Performance optimization methods include cached recommendation results, asynchronous processing, distributed computing and SQL query optimization, thereby improving recommendation efficiency and user experience.

When choosing a suitable PHP framework, you need to consider comprehensively according to project needs: Laravel is suitable for rapid development and provides EloquentORM and Blade template engines, which are convenient for database operation and dynamic form rendering; Symfony is more flexible and suitable for complex systems; CodeIgniter is lightweight and suitable for simple applications with high performance requirements. 2. To ensure the accuracy of AI models, we need to start with high-quality data training, reasonable selection of evaluation indicators (such as accuracy, recall, F1 value), regular performance evaluation and model tuning, and ensure code quality through unit testing and integration testing, while continuously monitoring the input data to prevent data drift. 3. Many measures are required to protect user privacy: encrypt and store sensitive data (such as AES

Use Seaborn's jointplot to quickly visualize the relationship and distribution between two variables; 2. The basic scatter plot is implemented by sns.jointplot(data=tips,x="total_bill",y="tip",kind="scatter"), the center is a scatter plot, and the histogram is displayed on the upper and lower and right sides; 3. Add regression lines and density information to a kind="reg", and combine marginal_kws to set the edge plot style; 4. When the data volume is large, it is recommended to use "hex"

The core of PHP's development of AI text summary is to call external AI service APIs (such as OpenAI, HuggingFace) as a coordinator to realize text preprocessing, API requests, response analysis and result display; 2. The limitation is that the computing performance is weak and the AI ecosystem is weak. The response strategy is to leverage APIs, service decoupling and asynchronous processing; 3. Model selection needs to weigh summary quality, cost, delay, concurrency, data privacy, and abstract models such as GPT or BART/T5 are recommended; 4. Performance optimization includes cache, asynchronous queues, batch processing and nearby area selection. Error processing needs to cover current limit retry, network timeout, key security, input verification and logging to ensure the stable and efficient operation of the system.

1. PHP mainly undertakes data collection, API communication, business rule processing, cache optimization and recommendation display in the AI content recommendation system, rather than directly performing complex model training; 2. The system collects user behavior and content data through PHP, calls back-end AI services (such as Python models) to obtain recommendation results, and uses Redis cache to improve performance; 3. Basic recommendation algorithms such as collaborative filtering or content similarity can implement lightweight logic in PHP, but large-scale computing still depends on professional AI services; 4. Optimization needs to pay attention to real-time, cold start, diversity and feedback closed loop, and challenges include high concurrency performance, model update stability, data compliance and recommendation interpretability. PHP needs to work together to build stable information, database and front-end.
