Description of Python black magic

Source: Internet
Author: User
Tags access properties

IntroductionDescriptors (descriptor) is PythonA profound but important black magic in the language, which is widely used in the kernel of the Python language, and mastering the descriptor will add an extra skill to the Python Programmer's Toolbox. In this article I will describe the definition of descriptors and some common scenarios, and at the end of the text I will add __getattr,__getattribute__, __getitem__ these three magic methods that also involve property access. Definition of Descriptor
Descr__get__ (self, obj, Objtype=none)--Value descr.__set__ (self, obj, value)--None descr.__delete__ (self, OB j)--None
This class can be called a descriptor class as long as an object attribute (object property) defines any of the three methods above. Descriptor BasicsIn this example we create a revealacess class and implement the __get__ method, which can now be called a descriptor class.
Class Revealaccess (object): Def __get__ (self, obj, objtype): Print ("Self" revealaccess: {} '. Format (self)) Print (' self: {}\nobj: {}\nobjtype: {} '. Format (self, obj, ObjType)) class MyClass (object): x = revealaccess ( ) def test (self): print (' MyClass: {} '. Format (self))
  EX1 Instance PropertiesLet's take a look at the meaning of each parameter of the __get__ method, in the following example, the self is an instance of the Revealaccess class X,obj the instance of the MyClass class M,objtype as the name implies 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: <__main__. MyClass object at 0x7f19d4e42160> >>> m.x-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 PropertiesIf the attribute x is accessed directly through the class, then obj is directly none, which is better understood, 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 descriptors Descriptor triggeringIn the above example, we have enumerated the use of descriptors from the perspective of instance properties and class attributes, so let's examine the internal principle in detail: If you access the instance properties, you actually call the __getattribute__ method of the base class object.  In this method, OBJ.D is translated into type (obj). __dict__[' d '].__get__ (obj, type (obj)). If access to a class property is equivalent to calling the __getattribute__ method of the meta-class type, it translates cls.d into cls.__dict__[' d '].__get__ (None, CLS), where __get__ ()  The obj is none because the instance does not exist. A brief talk about the __getattribute__ magic method, this method when we access the properties of an object will be unconditionally called, detailed details such as and __getattr, __getitem__ the difference I will be in articleAt the end of the month to make an additional supplement, we do not delve into the moment. Descriptor PrecedenceFirst, the descriptor is divided into two types: If an object defines both the __get__ () and the __set__ () method, the descriptor is called the data descriptor.  If an object defines only the __get__ () method, this descriptor is called the Non-data descriptor. There are four things we do when we access properties:
Data Descriptor Instance Dict non-data Descriptor __getattr__ ()
Their priority size is: Data descriptor > Instance dict > Non-data descriptor > __getattr__ () What does that mean? That is, if the instance object obj appears with the same name as data descriptor->d and instance ATTRIBUTE-&GT;D,OBJ.D access to attribute D, because the data descriptor has a higher priority, Python calls type (obj). __dict__[' d '].__get__ (obj, type (obj)) instead of calling obj.__dict__[' d ']. However, if the descriptor is a non-data descriptor,python the obj.__dict__[' d ' will be called. PropertyA descriptor class is defined every time a descriptor is used, which can seem tedious.  Python provides a concise way to add a data descriptor to a property. Property (Fget=none, Fset=none, Fdel=none, Doc=none)-Property attribute Fget, Fset and Fdel are class getter, Setter and Deleter methods. We use the following example to illustrate how the property is used:
Class Account (object): Def __init__ (self): Self._acct_num = None def get_acct_num (self): retur          n 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 Getter,acct.acct_num = value will call Setter,del Acct_num.acct_num will call deleter.
>>> acct = account () >>> Acct.acct_num = $ >>> acct.acct_num 1000
Python also provides a @property adorner, which can be used to create attributes for simple scenarios. A Property object has the Getter,setter and deleter adorner methods that can be used to create a copy of the property through the corresponding accessor function of the decorated function.
Class Account (object): Def __init__ (self): Self._acct_num = None @property # The _acct_num proper Ty.      The decorator creates a Read-only property Def acct_num (self): return self._acct_num @acct_num. Setter # The _acct_num property setters makes the property writeable Def set_acct_num (self, value): Self._acct_n Um = value @acct_num. deleter def del_acct_num (self): del self._acct_num
If you want to make the property read-only, simply remove the setter method. creating descriptors at run timeWe can add property properties at run time:
  class Person (object):    def AddProperty (self, attribute):        # Create local Sette R and Getter with a particular attribute name        getter = lambda Self:self._getproperty (attribu TE)        setter = lambda self, value:self._setproperty (attribute, value)       & nbsp;# Construct property attribute and add it to the class        setattr (self.__class__, attribute , property (Fget=getter,                           &NB Sp                        fset=setter, \       & nbsp                          ,         &NB Sp        doc= "auto-generated method")    def _setproperty (self, attribute, value):     &NBsp  print ("Setting: {} = {}". Format (attribute, value))        setattr (self, ' _ ' + attribute, value . Title ())    def _getproperty (self, attribute):        print ("Getting: {}". Format (attrib Ute)        return getattr (self, ' _ ' + attribute) >>> user = person () >>> user. AddProperty (' name ') >>> user.addproperty (' phone ') >>> User.Name = ' John smith ' Setting:name = John S Mith >>> user.phone = ' 12345 ' setting:phone = 12345 >>> user.name getting:name ' John Smith ' > >> user.__dict__ {' _phone ': ' 12345 ', ' _name ': ' John Smith '}
   static methods and class methodsWe can use descriptors to emulate the implementation of @staticmethod and @classmethod in Python. Let's start by looking at the following table: Static MethodsFor static method F. C.F and C.F are equivalent and are directly queried for object.__getattribute__ (C, ' F ') or object.__getattribute__ (C, ' F ').  One obvious feature of static methods is that there is no self variable. What is the use of static methods? Suppose there is a container class that handles specialized data, which provides methods to calculate the average, median, and so on, which are dependent on the corresponding data.  However, there may be some methods in the class that do not depend on the data, and we can declare these methods as static methods at this time, which can also improve the readability of the code. Use non-data descriptors to simulate the implementation of a static method:
Class Staticmethod (object): Def __init__ (self, f): Self.f = f def __get__ (self, obj, Objtype=none): Return SELF.F
Let's apply it:
Class MyClass (object): @StaticMethod def get_x (x): return x print (myclass.get_x) # output:100
  class MethodPython's @classmethod and @staticmethod are somewhat similar, but they are somewhat different, and need to be used when some methods simply want to get a reference to the class and don't care about the corresponding data in the class Classmethod. Use non-data descriptors to simulate the implementation of a class method:
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
  Other methods of magicThe first time I touched the Python magic method, I was also troubled by the distinction between __get__, __getattribute__, __getattr__, __getitem__, which are all related to property access and the Magic method that overrides __getattr_  _,__getitem__ to construct a collection class of their own is very common, let's look at some examples of their application. __getattr__ python The default access to a class/instance of a property is called through __getattribute__, __getattribute__ will be unconditionally called, will not find the words would call __getattr__.  If we were to customize a class, we would normally not rewrite __getattribute__, but should rewrite __getattr__ and rarely see the rewrite __getattribute__. As you can see from the output below, __getattr__ is called when a property cannot be found by __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 (). A call __getattribute__ out[2]: ' Call __getattr__ '
  ApplicationFor the default dictionary, Python only supports access in the form of obj[' foo '] and does not support the form of obj.foo, and we can rewrite the __getattr__ to let the dictionary also support the Access form of obj[' Foo ', which is a very classic use:
class Storage (dict):     ""     A Storage object is Like a dictionary except ' Obj.foo ' can is used     In addition to ' obj[' foo '.     ""    def __getattr__ (self, key):        try:       &NBS P    return Self[key]        except Keyerror as K:           &NBS    P;raise Attributeerror (k)    def __setattr__ (self, Key, value):        self[key] = value    def __delattr__ (self, key):        try:           &NBSP;D El Self[key]        except Keyerror as K:            raise Attribute Error (k)    def __repr__ (self):        return ' <storage ' + dict.__repr__ (self) + ' &G t; '!
Let's use our custom version of the Enhanced Dictionary:
>>> 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 is used to get the elements in the object in the form of subscript [], and below we implement a list by overriding __getitem__.
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]
This implementation is very humble, does not support slice and step functions, please the reader to improve themselves, here I will not repeat. Application  The following is a reference to the use of the requests library for __getitem__, and we have customized a dictionary class that ignores property capitalization. The program is a little complicated, and I'll explain a little bit: because it's simple here, there's no need to use a descriptor, so instead of using the @property adorner, the Lower_keys function 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, and later we access a property that first converts the key to lowercase, and then does not directly access the instance dictionary, but instead accesses the dictionary self._lower_keys to find it. Assignment/delete operation because the instance dictionary changes, in order to maintain Self._lower_keys and instance dictionary synchronization, first clear the contents of Self._lower_keys, and then we re-look for the key again when we call __getitem__ Will re-create a new self._lower_keys.
  Class Caseinsensitivedict (Dict):     @property    def Lower_keys (self):       &N Bsp;if not hasattr (self, ' _lower_keys ') or not Self._lower_keys:            self._lower_ke ys = Dict ((K.lower (), K) for K in Self.keys ())        return self._lower_keys    def _CL Ear_lower_keys:        if hasattr (self, ' _lower_keys '):           & Nbsp;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__ (SE LF, key, value):        dict.__setitem__ (self, key, value)        self._clear_ Lower_keys ()   &NBSp;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:         &NBSP ;  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

Description of Python black magic

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.