Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Python descriptor

2025-04-12 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "how to use Python descriptors". In daily operation, I believe many people have doubts about how to use Python descriptors. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "how to use Python descriptors". Next, please follow the editor to study!

Introduction

Descriptors (descriptor) is an esoteric but important dark magic in the Python language. It is widely used in the kernel of the Python language. Mastering descriptors will add an additional skill to the Python programmer's toolbox.

Definition of descriptor descr__get__ (self, obj, objtype=None)-- > valuedescr.__set__ (self, obj, value)-- > Nonedescr.__delete__ (self, obj)-- > None

As long as an object attribute (object property) defines any of the above three methods, then this class can be called a descriptor class.

Descriptor basis

In the following example, we created a RevealAcess class and implemented the _ _ get__ method, which can now 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 Properties

Next, let's take a look at the meaning of the parameters of the _ _ get__ method. In the following example, self is the instance of the RevealAccess class, the instance of the RevealAccess class, and the instance of the MyClass class, mjjtype, is the MyClass class itself. As you can see from the output statement, the m. X access descriptor x calls the _ _ get__ method.

> m = MyClass () > > m.test () self in MyClass: > m.xself in RevealAccess: self: obj: objtype:

EX2 Class Properties

If the attribute x is accessed directly through the class, then the obj is directly None, which is easier to understand, because there is no instance of MyClass.

> MyClass.xself in RevealAccess: self: obj: Noneobjtype: principle descriptor trigger of descriptor

In the above example, we enumerate the use of descriptors from the perspective of instance properties and class properties, respectively. Let's take a closer look at the internal principles:

If you are accessing the instance properties, the _ _ getattribute__ method of the base class object is actually called, in which the obj.d is translated into type (obj). _ _ dict__ ['d']. _ _ get__ (obj, type (obj)).

If you are accessing a class attribute, it is equivalent to calling the _ _ getattribute__ method of the metaclass type, which translates cls.d into cls.__dict__ ['d']. _ _ get__ (None, cls), where the obj of _ _ get__ () is None, because there is no instance.

Briefly talk about the _ _ getattribute__ magic method. This method will be called unconditionally when we access the properties of an object. The details such as the difference between _ _ getattr and _ _ getitem__ will be added at the end of the article. We will not delve into it for the time being.

Descriptor priority

First, there are two types of descriptors:

If an object defines both the _ _ get__ () and _ _ set__ () methods, the descriptor is called data descriptor.

If an object defines only the _ _ get__ () method, the descriptor is called non-data descriptor.

There are four situations when we access properties:

Data descriptor

Instance dict

Non-data descriptor

_ _ getattr__ ()

Their priority sizes are:

Data descriptor > instance dict > non-data descriptor > _ _ getattr__ ()

What does this mean? That is, if data descriptor- > d and instance attribute- > dj.d with the same name appear in the instance object obj to access attribute d, because data descriptor has a higher priority, Python will call type (obj). _ dict__ ['d']. _ _ get__ (obj, type (obj)) instead of obj.__dict__ ['d']. But if the descriptor is a non-data descriptor,Python, obj.__dict__ ['d'] is called.

Property

It seems very tedious to define a descriptor class every time you use a descriptor. Python provides a concise way to add data descriptors to attributes.

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. Let's use the following example to illustrate how to use Property:

Class Account (object): def _ init__ (self): self._acct_num = Nonedef get_acct_num (self): return self._acct_numdef set_acct_num (self, value): self._acct_num = valuedef 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 getter,acct.acct_num = value, setter,del acct_num.acct_num will call deleter.

> acct = Account () > acct.acct_num = 1000 > acct.acct_num1000

Python also provides the @ property decorator, which you can use to create attributes for simple application scenarios. A property object has getter,setter and deleter decorator methods that 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 propertydef acct_num (self): return self._acct_num @ acct_num.setter# the _ acct_num property setter makes the property writeabledef set_acct_num (self, value): self._acct_num = value @ acct_num.deleterdef del_acct_num (self): del self._acct_num

If you want the property to be read-only, you just need to remove the setter method.

Create descriptors at run time

We can add the property attribute at run time:

Class Person (object): def addProperty (self, attribute): # create local setter and getter with a particular attribute namegetter = lambda self: self._getProperty (attribute) setter = lambda self, value: self._setProperty (attribute, value) # construct property attribute and add it to the classsetattr (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.nameGetting: name'John Smith' > user.__dict__ {'_ phone': '12345,' _ name': 'John Smith'} static and class methods

We can use descriptors to simulate the implementation of @ staticmethod and @ classmethod in Python. Let's first take a look at the following table:

TransformationCalled from an ObjectCalled from a Classfunctionf (obj, * args) f (* args) staticmethodf (* args) f (* args) classmethodf (type (obj), * args) f (klass, * args) static method

For static method f. C. f and C. f are equivalent, both directly query object.__getattribute__ (c,'f') or object.__getattribute__ (C,'f'). An obvious feature of static methods is that there are no self variables.

What is the use of static methods? Suppose there is a container class that handles specialized data, which provides some methods for finding statistical data, such as averages, median, and so on, all of which depend on the corresponding data. However, there may be some methods in the class that do not rely on the data, so we can declare these methods as static methods, which can also improve the readability of the code.

Use non-data descriptors to simulate the implementation of static methods:

Class StaticMethod (object): def _ init__ (self, f): self.f = fdef _ get__ (self, obj, objtype=None): return self.f

Let's apply it:

Class MyClass (object): @ StaticMethoddef get_x (x): return xprint (MyClass.get_x (100)) # output: class methods

The use of @ classmethod of Python is somewhat similar to that of @ staticmethod, but there is still some difference. When some methods only need to get a reference to the class and do not care about the corresponding data in the class, they need to use classmethod.

Use non-data descriptors to simulate the implementation of class methods:

Class ClassMethod (object): def _ init__ (self, f): self.f = fdef _ get__ (self, obj, klass=None): if klass is None: klass= type (obj) def newfunc (* args): return self.f (klass, * args) return newfunc other magic methods

* when I come into contact with Python magic methods, I am also troubled by the difference between _ _ get__, _ _ getattribute__, _ _ getattr__,__getitem__. They are all magic methods related to property access, in which it is very common to override _ _ getattr__,__getitem__ to construct a collection class of my own. Let's take a look at their application through some examples.

_ _ getattr__

By default, Python accesses a property of the class / instance through _ _ getattribute__. _ _ getattribute__ will be called unconditionally, and _ _ getattr__ will be called if it is not found. If we are going to customize a class, normally we should not rewrite _ _ getattribute__, but should rewrite _ _ getattr__,. We rarely see the case of rewriting _ _ getattribute__.

As you can see from the output below, _ _ getattr__ is called when an attribute cannot be found through _ _ getattribute__.

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 (). Acall _ getattribute__Out [2]:' call _ getattr__' application

For the default dictionary, Python only supports access in the form of obj ['foo'], but not in the form of obj.foo. We can rewrite _ _ getattr__ to make the dictionary also support the access form of obj [' foo'], which is a very classic and common usage:

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] = valuedef _ delattr__ (self, key): try:del Self [key] except KeyError as k:raise AttributeError (k) def _ repr__ (self): return''

Let's use our custom enhanced dictionary:

> s = Storage (axi1) > s ['a'] 1 > s.a1 > s.a = 2 > s ['a'] 2 > del s.a > s.a...AttributeError: 'axia`s

Getitem is used to get the elements in the object in the form of subscript []. Let's implement our own list by overriding _ _ getitem__.

Class MyList (object): def _ init__ (self, * args): self.numbers = argsdef _ getitem__ (self, item): return self.numbers my_list = MyList (1, 2, 3, 4, 6, 5, 3) print my_list [2]

This implementation is very simple and does not support functions such as slice and step. Readers are asked to improve on their own. I will not repeat them here.

Application

The following is a use of _ _ getitem__ in the reference requests library, where we customize a dictionary class that ignores attribute case.

The program is a little complicated, let me explain a little bit: because it is relatively simple here, there is no need to use descriptors, so the @ property decorator is used instead. The function of lower_keys is to convert all the keys in the instance dictionary to lowercase and store them in the dictionary self._lower_keys. The _ _ getitem__ method is overridden. Later, when we access a property, we first convert the key to lowercase, and then we don't access the instance dictionary directly, but access the dictionary self._lower_keys to find it. During the assignment / deletion operation, because the instance dictionary will be changed, in order to keep the self._lower_keys synchronized with the instance dictionary, clear the contents of self._lower_keys first, and then create a new self._lower_keys when we call _ _ getitem__ when we re-find the key.

Class CaseInsensitiveDict (dict): @ propertydef 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_keysdef _ clear_lower_keys (self): if hasattr (self,'_ lower_keys'): self._lower_keys.clear () def _ contains__ (self) Key): return key.lower () in self.lower_keysdef _ 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

Let's call this class:

> 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, the study on "how to use Python descriptors" is over. I hope you can solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report