Python object protocol

Keywords: Python

Original text: https://www.cnblogs.com/JanSN/p/12540247.html

Because Python is a dynamic language and the Concept of Duck Typing is all over it, the Concept does not take type constraints as the carrier, but uses a Concept called protocol.

In Python   I need to call one of your methods, and you happen to have this method.
For example, in string formatting, if there is a placeholder% s, Python will automatically call the corresponding object according to the string conversion protocol  __ str__ ()   method.

classification

① . type conversion protocol

except  __ str__ ()   There are other ways,
such as

  • __repr__(),
  • __init__(),
  • __long__(),
  • __float__(),
  • __ nonzero__ ()   Etc., collectively referred to as type conversion protocol.

In addition to the type conversion protocol, there are many other protocols.

② Protocol for comparing sizes

This agreement depends on  __ cmp__ (), similar to the C language library function CMP,
Returns 0 when the two are equal   self < other   Returns a negative value when, and a positive value if not.

Because of this complexity, Python has

  • __ eq__ ()   Equal to
  • __ ne__ ()   Not equal to
  • __ lt__ ()   Less than
  • __ gt__ ()   greater than
    And other methods to determine the comparison size.

This is Python's right  ==,!=,<  and  >  Support mechanism for overloading of operators such as.

③ Protocol related to value type

There are many functions in this class. Basically, as long as you implement a few methods, you can basically simulate numerical types.
However, it is also necessary to mention a unique concept in Python: inverse operation (?).
similar  __ radd__ ()   All numeric operators and bitwise operators are supported, and the rules are all prefixed with r.

④ . container type agreement

The following methods need cooperation   A container property accessed by an available index in the class   use

The protocol of the container is very simple. Since it is a container, there must be a protocol to query the number of objects contained,
In Python, support built-in functions   len(), passed  __ len__ ()   To finish.

  • __ getitem__ ()   Read View usage examples

  • __ setitem__ ()   Write, (use example similar to above)

  • __ delitem__ ()   delete
    It's also easy to understand.

  • __ iter__ ()   The iterator protocol is implemented,

  • __ reversed__ ()   Provides access to built-in functions   reversed()   Support.

The most distinctive feature of container types is the support for membership judges in and not in. This method is called

  • __ contains__ (), as long as this function is supported, the in and not in operators can be used.

⑤ . callable object protocol

Callable objects are similar to function objects,

  • __ call__ ()  : Let class instances behave like functions, so that each function call can be different.
class Functor(object):

    def __init__(self, context):
        self._context = context

    def __call__(self):
        print("do something with {}".format(self._context))

lai_functor = Functor("lai")
yong_functor = Functor("yong")
lai_functor()   # Call object, execute__ call__ () actions defined in the protocol
yong_functor()

⑥ Hashable object protocol

  • __ hash__ ()  : The definition in the protocol is called when the object needs to generate hashCode

Supported by this method   hash()   This built-in function is very useful when creating your own type,
Because only types that support hashable protocols can be used as the key type of dict (but as long as new classes inherited from object are supported by default)

⑦ , attribute operation protocol and descriptor protocol

The following methods are called when the properties of a class are manipulated

  • __getattr__():
    If the attribute lookup fails in the instance and the corresponding class (through _dict), the _getattr function of the class is called. View usage examples

  • __setattr__()
    Assign a value to an existing property. View usage examples

  • __delattr__()
    The method of deleting attributes should be rarely used. I won't know more here

⑧ There is also the context manager protocol, that is, the support for the with statement**

The agreement passed

  • __enter__()
  • __exit__()   Two methods are used to clean up resources to ensure that resources will be cleaned up normally under any circumstances.

Unlike the interfaces in C + +, Java and other languages, the protocol is more like a declaration and has no language binding force.

Use example

__getattr__()

 

source

__Functions of getattr_ function:

If the attribute lookup fails in the instance and the corresponding class (through _dict), the _getattr function of the class will be called;

If this function is not defined, the   AttributeError   Abnormal.
Thus, _getattr_ must be the last step for attribute lookup

  • Example 1
class A(object):
    def __init__(self, a, b):
        self.a1 = a
        self.b1 = b
        print('init')

    def mydefault(self, *args):
        print('args:' + str(args[0]))

    def __getattr__(self, attr_name):
        print("not exist func:", attr_name)
        return self.mydefault


a1 = A(10, 20)
a1.fn1(33)
a1.fn2('hello')     # Run it yourself

Output:

init
not exist func: fn1
args:33
not exist func: fn2
args:hello
  • Example 2: classic example  __ getattr_ () virtual dictionary object
class ObjectDict(dict):
    def __init__(self, *args, **kwargs):
        super(ObjectDict, self).__init__(*args, **kwargs)

    def __getattr__(self, attr_name):
        value = self[attr_name]         # 
        if isinstance(value, dict):
            value = ObjectDict(value)
        return value


if __name__ == '__main__':
    od = ObjectDict(asf={'a': 1}, d=True)
    print(od.asf)       # {'a': 1}
    print(od.asf.a)     # 1
    print(od.d)         # True

 

__setattr__()

 

source

It should be noted that the assignment statements of all attributes will be intercepted
If this method is defined,
self.attr = value   Will become   self.__setattr__("attr", value)

It should be noted that:
Cannot be used when an attribute is assigned within the _setattr _ _method   self.attr = value
Because he will call again   self.__setattr__("attr", value)
Will form   Infinite recursive loop, resulting in stack overflow exception.

Correct practice:
Should pass   Index the attribute dictionary to assign values   Any instance property, that is, using

self.__dict__['name'] = value

If the class customizes the _setattr method, _setattr will be called when an attempt is made to obtain an attribute assignment through an instance.
Routine assignment of instance attributes. The assigned attributes and values will be stored in the   Instance attribute dictionary _dict__   Yes.

Instance attribute dictionary  __ dict__

class ClassA(object):

    def __init__(self, classname):
        self.classname = classname

insA = ClassA('ClassA')
print(insA.__dict__)    # {'classname': 'ClassA'}

insA.tag = 'insA'    
print(insA.__dict__)    # {'tag': 'insA', 'classname': 'ClassA'}

 

If the class customizes _setattr _, it will be called by the assignment to the instance property.

The same is true for self.attr in the class definition, so the assignment operation of self.attr under _setattr will lead to wireless recursive call _setattr.

Implementing _setattr by yourself is very risky. Generally, it inherits the _setattr method of the object class.

class ClassA(object):
    def __init__(self, classname):
        self.classname = classname  # Call _setattr here__

    def __setattr__(self, name, value):
        # self.name = value         # If you write this here, there will be infinite recursion
        print('call __setattr__')

insA = ClassA('ClassA') # call __setattr__
print(insA.__dict__)    # {}

insA.tag = 'insA'       # call __setattr__
print(insA.__dict__)    # {}

 

__getitem__()

 

source

If a class defines an attribute as a sequence, you can use _getitem _ () to output an element in the sequence attribute

class FruitShop():
     def __getitem__(self, i):
         return self.fruits[i]  # When traversing the FruitShop instance, the self.fruits sequence is traversed

if __name__ == "__main__":
    shop = FruitShop()
    shop.fruits = ["apple", "banana"]
    print(shop[1])          # banana
    print("----")
    for item in shop:       # Traversal is the self.fruits sequence
        print(item)

Output:

banana
----
apple
banana

Posted by Gish on Sat, 27 Nov 2021 21:02:43 -0800