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__()
__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__()
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__()
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