Advance Python 03: drill down on classes and objects

Introduce:

  • Duck type and polymorphism

  • Abstract base class (abc module)

  • The difference between isinstance and type

  • Class variable and instance variable

  • Class properties and instance properties, and search order

  • Static methods, class methods, object methods, and parameters

  • Data encapsulation and private properties

  • Introspection mechanism of python object

  • Is super really calling the parent class

  • mixin inheritance cases (django, rest, framework)

  • with statement in Python

  • contextlib simplified context manager

1, Duck type and polymorphism

Duck type

It can be called a duck to swim and walk like a duck

In this way:

  • In Python, sometimes we need an object with a certain function (for example, duck barking). We can check whether the object meets our needs by judging whether it is a duck or not
  • But think about it carefully, there are some defects, because what we really need is the method of duck barking. An object, whether it is a duck or not, as long as it can bark like a duck.

polymorphic

The type at the time of definition is different from the type at the time of runtime, which becomes polymorphic. The concept of polymorphism is applied to strongly typed languages such as Java and C# while Python advocates "duck type".

  • Python polymorphism is to weaken the type, focusing on whether the object parameters have specified attributes and methods. If so, it will be determined to be appropriate, regardless of whether the object type is correct.
  • Python "duck type"

About Python's three object-oriented brothers (three features): encapsulation, polymorphism and inheritance

Share a few articles about big guys:

Object oriented programming (detailed explanation of python version)_ Chen Xi should work hard - CSDN blog_ python object oriented programming

Object oriented programming (inheritance) - Python Version (case explanation)_ Chen Xi should work hard - CSDN blog

Object oriented programming (encapsulation, polymorphism) Python Version (Demo)_ Chen Xi should work hard - CSDN blog

Example code:

class Cat:
    def say(self):
        print('I am a cat.')


class Dog:
    def say(self):
        print('I am a dog.')


class Duck:
    def say(self):
        print('I am a duck.')


# Python is more flexible. As long as the say method is implemented, polymorphism is realized
animal = Cat
animal().say()

# To achieve polymorphism, you only need to define the same method
animal_list = [Cat, Dog, Duck]
for an in animal_list:
    an().say()

"""
class Animal:
    def say(self):
        print('I am a animal.')

# You need to inherit Animal and override the say method
class Cat(Animal):
   def say(self):
       print('I am a cat.')

# Type needs to be specified for definition in Java
Animal an = new Cat()
an.say()
"""

li1 = ['i1', 'i2']
li2 = ['i3', 'i4']

tu = ('i5', 'i6')
s1 = set()
s1.add('i7')
s1.add('i8')

# Change the concept. Not only the list is passed in, but even the iterable object is implemented by yourself
li1.extend(li2)  # iterable
li1.extend(tu)
li1.extend(s1)
print(li1)

Operation results:

I am a cat.
I am a cat.
I am a dog.
I am a duck.
['i1', 'i2', 'i3', 'i4', 'i5', 'i6', 'i7', 'i8']

The process has ended with exit code 0
  • To achieve polymorphism, you only need to define the same method
  • Magic function makes full use of the characteristics of duck type, just insert the function into the type

2, Abstract base class (abc module)

1. Concept

Abstract base class of Python:

It refers to a class that must inherit its subclasses to implement the abstract methods it requires.

Or say this:

Abstract base class provides an interface, but it does not implement the interface. It needs to be completed by subclasses.

It feels like the boss. He only tells you to complete project A. after you receive project a (inherit), you can complete it yourself.

Abstract base class features:

  • Abstract base class cannot be instantiated
  • Variables have no type restrictions and can point to any type
  • Abstract base classes and magic functions form the basis of python, that is, protocol

Abstract methods are defined in the abstract base class. Classes that inherit the abstract base class must implement these methods

2. Application scenario

There are generally two application scenarios:

Scenario 1: want to judge the type of an object

# Check whether a class has some method
class Company:
    def __init__(self, name):
        self.name = name

    def __len__(self):
        return len(self.name)


company = Company('Linda Process Ltd.')
print(hasattr(company, '__len__'))

# In some cases, we want to determine the type of an object
from collections.abc import Sized
print(isinstance(company, Sized))

Operation results:

True
True

The process has ended with exit code 0

Supplement:

hasattr () Function:
Used to determine whether a class instance object contains a property or method with a specified name.. The syntax format of this function is as follows:. hasattr (obj, name) among obj It refers to the instance object of a class, name Represents the specified property name or method name.
isinstance () Function:
Used to determine whether an object is a known type, similar to type ().  
type () The subclass will not be considered as a parent type, and the inheritance relationship will not be considered.
isinstance () You will think that a subclass is a parent type and consider the inheritance relationship.

Scenario 2: force subclasses to implement certain methods

For example, as mentioned at the beginning, the boss asked us to implement some interfaces. How can we force these interfaces to be completed?

You can use an abstract base class because it must let subclasses that inherit it implement the methods it requires

To facilitate understanding, we implement abstract base classes in two ways:

(1) We first define the abstract base class ourselves

# We need to force a subclass to implement certain methods
# For example, we have implemented a web framework that integrates cache (redis, cache, memory cache)
# You need to design an abstract base class, and the specified subclass must implement some methods
# How to simulate an abstract base class

class CacheBase():
    def get(self, key):
        raise NotImplementedError

    def set(self, key, value):
        raise NotImplementedError


class RedisCache(CacheBase):
    pass


redis_cache = RedisCache()
redis_cache.set("key", "value")

Operation results:

The inherited class must implement the methods of the abstract class

Therefore, raise notimplemented error is triggered

NotImplementedError

The process has ended with exit code 1

(2) Using abc module

# We need to force a subclass to implement certain methods
# For example, we have implemented a web framework that integrates cache (redis, cache, memory cache)
# You need to design an abstract base class, and the specified subclass must implement some methods
# How to simulate an abstract base class

import abc


class CacheBase(metaclass=abc.ABCMeta):  # Metaclass will be discussed in metaclass programming

    @abc.abstractmethod
    def get(self, key):
        pass

    @abc.abstractmethod
    def set(self, key, value):
        pass


class MemoryCache(CacheBase):
    pass
 

redis_cache = MemoryCache()
redis_cache.set("key", "value")

Operation results:

If an error is reported, the inherited class must implement the method of the abstract class

TypeError: Can't instantiate abstract class MemoryCache with abstract methods get, set

The process has ended with exit code 1

be careful:

In fact, abstract base classes are not commonly used

Abstract base classes are prone to over design, and Mixin is recommended for multi inheritance

Share a few articles about big guys:

Python official: abc - abstract base class - Python 3.10.2 document

python abstract base class - Zhihu (zhihu.com)

3, The difference between isinstance and type

  • isinstance will find the inheritance chain
  • type only determines the memory address of the variable
class A:
    pass


class B(A):
    pass


b = B()
print(isinstance(b, B))  # True
print(isinstance(b, A))  # True

# is determines the meaning of id
print(type(b) is B)  # True
print(type(b) is A)  # False

Operation results:

True
True
True
False

The process has ended with exit code 0

4, Class variable and instance variable

  • Class variable definition and use
  • Definition and use of instance variables
  • Class variables are shared by all instance variables

Example 1:

class A:
    aa = 1  # Class variable

    def __init__(self, x, y):
        self.x = x
        self.y = y


a = A(2, 3)
print(a.x, a.y, a.aa)  # 2 3 1

A.aa = 11
a.aa = 100
print(a.x, a.y, a.aa)  # 2 3 100
print(A.aa)  # 11

b = A(3, 5)
print(b.aa)  # 11

Operation results:

2 3 1
2 3 100
11
11

The process has ended with exit code 0

Example 2:

class A:
    name = "A"

    def __init__(self):
        self.name = "obj"


a = A()
print(a.name)

Operation results:

obj

The process has ended with exit code 0

5, Class properties and instance properties, and search order

1. Class properties and instance properties

Class properties and instance properties

  • Class properties: variables and methods defined in the class
  • Instance properties:__ init__ Defined in

In the case of the previous chapter, we can easily understand the relationship between class variables and instance variables

But when it comes to attributes, especially in the case of multiple inheritance, it becomes complex

2. MRO algorithm

MRO

: (Method Resolution Order) Method Resolution Order.

Concept supplement:

Classic vs New

About class“

Python 2. The default classes in X are classic classes, and only those that explicitly inherit object are new classes

(it seems that Python 2.2 was a classic class before)

Python 3.x is a new type of class by default, and there is no need to explicitly inherit object

So what kind of search order is used for class attributes and instance attributes???

1. When the class is a classic class, in the case of multiple inheritance, it will be searched according to the depth first

2. When the class is a new class, in the case of multiple inheritance, it will be searched according to breadth first

However, note:

Both DFS and BFS have defects in the face of multiple inheritance

In the past, Python has adopted DFS and BFS, but

Since Python 2 3. From now on, MRO C3 algorithm (C3 algorithm) is used

Let's briefly understand these search algorithms through some cases:

  • DFS
  • BFS
  • MRO C3

(1) Depth first DFS

Example 1:

  • The search order is a - > b - > D - > C - > E
  • This kind of scene depth first is more appropriate

Example 2:

  • The search order is a - > b - > D - > C

  • In this scenario, when a method in D is overloaded in C, the search order is not appropriate

(2) Breadth first BFS

Example 1:

  • The search order is a - > b - > C - > D - > E

  • In this scenario, B inherits D, B and D are integrated, and D should precede C

Example 2:

  • The search order is a - > b - > C - > D

  • This kind of scene depth first is more appropriate

(3) MRO C3 algorithm

C3 algorithm is complex

Here are two examples

Example 1: Diamond inheritance

Example code:

class D:
    pass


class C(D):
    pass


class B(D):
    pass


class A(B,C):
    pass


if __name__ == '__main__':
    print(A.__mro__)
    

Operation results:

(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
  • A ==> B ==> C ==> D

Example 2: B and C inherit scenarios D and E respectively

Example code:

class D:
    pass


class E:
    pass


class C(E):
    pass


class B(D):
    pass


class A(B, C):
    pass


print(A.__mro__)

Operation results:

(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)

6, Static methods, class methods, object methods, and parameters

  • Static method @ staticmethod
  • Class method @ classmethod
  • Example method

The implementation of static methods and class methods in python depends on the modifier of python.

Let's first look at a piece of code. The specific definitions of these three methods are described later

Example code:

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    # Example method
    def tomorrow(self):
        self.day += 1

    # Static method
    @staticmethod
    def date_from_str(date_str):
        year, month, day = tuple(date_str.split('-'))
        return Date(int(year), int(month), int(day))

    # Class method
    @classmethod
    def date_from_string(cls, date_str):
        year, month, day = tuple(date_str.split('-'))
        return cls(int(year), int(month), int(day))

    def __str__(self):
        return '{year}/{month}/{day}'.format(year=self.year, month=self.month, day=self.day)


if __name__ == '__main__':
    new_day = Date(2020, 2, 20)
    new_day.tomorrow()
    print(new_day)

    date_str = '2022-2-5'
    print(Date.date_from_str(date_str))
    print(Date.date_from_string(date_str))

Operation results:

2020/2/21
2022/2/5
2022/2/5

The process has ended with exit code 0

Example method

Definition: no decorator.
The first parameter must be an instance object. The parameter name is generally agreed to be "self". It is used to pass the attributes and methods of the instance (or the attributes and methods of the class);

Call: can only be called by instance objects.

Class method

Definition: use decorator @ classmethod.
The parameter name of the method is "cls", which cannot be passed through the parameter name of the current class and method;

Call: both class and instance objects can be called.

Static method

Definition: use the decorator @ staticmethod.
Parameters are optional. There are no "self" and "cls" parameters, but any attribute and method of class or instance cannot be used in the method body;

Call: both class and instance objects can be called.

Summary:

parameterMethods that can be calledProperties that can be called
Example methodNo trimmer.
The first parameter must be an instance object, and the parameter name is generally agreed to be "self"
Instance method, class method, static methodClass attribute
Class methodUse the decorator @ classmethod.
The first parameter must be the current class object, and the parameter name is generally agreed as "cls"
Class method, static methodClass properties
Static methodUse the decorator @ staticmethod.
The parameters are optional. There are no "self" and "cls" parameters.
Instance method, class method, static method (called by class name)Class properties (called by class name)

7, Data encapsulation and private properties

Private properties and private methods

Set private properties and methods:

  • Precede the property name and method name with two underscores__
  • It is defined as private property and private method and cannot be inherited by subclasses

Private properties cannot pass through instances Private properties, and cannot be obtained through subclasses

Example code:

class Person(object):
    # Constructor
    def __init__(self, name):
        self.name = name
        self.__age = 18


temp = Person("Coder")
print(temp.name)  # Available
print(temp.__age)  # Not available, error: AttributeError

Operation results:

    print(temp.__age)
AttributeError: 'Person' object has no attribute '__age'
Coder

The process has ended with exit code 1

be careful:

But in fact, there is no real private in Python

Private properties can also be obtained in a variety of ways:

(1)

  • When naming properties and methods for privatization, some special processing is actually done to the name, which makes it inaccessible to the outside world
  • Processing method: add before the name_ Class name = >_ Class name__ Name, such as the example above: Print (obj. _person_age)

(2)

Example code:

class User:
    def __init__(self):
        self.__age = 18

    def get_age(self):
        return self.__age


if __name__ == '__main__':
    user = User()
    print(user.get_age())

    # print(user.__age)

    # _ class__attr, deformed
    print(user._User__age)

Operation results:

18
18

The process has ended with exit code 0
  • python does not strictly restrict the use of private attributes, which is a code writing specification

8, Introspection mechanism of python object

a key:

  • Concept of introspection mechanism
  • dir()

Introspection mechanism

Query the internal structure of the object through a certain mechanism

Common introspection mechanisms (function usage) in Python include:

  • dir()
  • type()
  • hasattr()
  • isinstance()

Through these functions, we can know the type of object when the program is running, judge whether there is a property of the object, and access the property of the object.

overview:

  • type() to judge the object type
  • dir(), get all the attributes and methods of the object with parameters; Returns a list of variables, methods, and defined types in the current scope without parameters
  • isinstance(), judge whether the object is a known type
  • hasattr(), judge whether the object contains corresponding attributes
  • getattr(), get object properties
  • setattr(), set object properties

Author: Treehl
Link: https://www.jianshu.com/p/5166427002a8
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

(1)dir()

  • dict
  • dir()

The dir () function is probably the most famous part of Python's introspection mechanism. It returns a sorted list of the property names of any object passed to it. If no object is specified, dir() returns the name in the current scope.

Without parameters:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']

With parameters:

dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

Example code:

class User:
    name = 'user'

class Student(User):
    def __init__(self):
        self.school_name = 'school'

if __name__ == '__main__':
    stu = Student()

    # Pass__ dict__  Query attributes, implemented in C language, optimized and faster
    print(stu.__dict__)
    stu.__dict__['age'] = 18
    print(stu.age)

    print(User.__dict__)

    print(dir(stu))
    

Operation results:

{'school_name': 'school'}
18
{'__module__': '__main__', 'name': 'user', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'school_name']

The process has ended with exit code 0

(2)type()

The type() function helps us determine whether the object is a string or an integer, or other types of objects.

(return the corresponding class type)

In [27]: type(123)
Out[27]: int

In [28]: type('123')
Out[28]: str

In [29]: type(None)
Out[29]: NoneType

(3)hasattr()

Object has properties, and the dir() function returns a list of these properties. However, sometimes we just want to test whether one or more attributes exist. If an object has a property that we are considering, we usually want to retrieve only that property. This task can be accomplished by the hasattr() and getattr() functions

Note: getattr, hasattr and setattr get the status of the object

In [1]: class Myobject(object):
   ...:     def __init__(self):
   ...:         self.x = 9
   ...:     def power(self):
   ...:         return self.x * self.x
   ...:

In [2]: obj = Myobject()

In [3]: obj.power()
Out[3]: 81

In [4]: hasattr(obj, 'x') # Is there an attribute x?
Out[4]: True

In [5]: hasattr(obj, 'y') # Is there an attribute y?
Out[5]: False

In [6]: setattr(obj, 'y') # There must be three parameters for setting the property, and the value is missing
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-3b0a0f22117d> in <module>()
----> 1 setattr(obj, 'y')

TypeError: setattr expected 3 arguments, got 2

In [7]: setattr(obj, 'y', 100) # Set the value of property y to 100

In [8]: hasattr(obj, 'y') # Is there an attribute y?
Out[8]: True

In [9]: getattr(obj, 'y') # Get property y!
Out[9]: 100

In [10]: obj.y # Get property y
Out[10]: 100

In [11]: obj.x # Get property x
Out[11]: 9

(4)isinstance()

You can use the isinstance() function to test an object to determine whether it is an instance of a particular type or custom class

Example:

Judge whether a data type is list

In [37]: a = [1, 2, 3]

In [38]: isinstance(a, list)
Out[38]: True

The most important purposes of introspection are to let python answer us:

  1. What is the object name?
  2. What can objects do?
  3. What type of object is it?
  4. What are some basic information of the object?

9, Is super really calling the parent class

1. Function of super

When there is an inheritance relationship, it is sometimes necessary to call the parent class method in the subclass. The simplest way is to convert the object call to the class call. It is important to note that the self parameter needs to be explicitly passed, for example:

class FooParent:
  def bar(self, message):
    print(message)
class FooChild(FooParent):
  def bar(self, message):
    FooParent.bar(self, message)
>>> FooChild().bar("Hello, Python.")
Hello, Python.

This has some disadvantages. For example, if the parent class name is modified, multiple modifications will be involved in the subclass. In addition, Python is a language that allows multiple inheritance. The methods shown above need to be written repeatedly during multiple inheritance, which is cumbersome.

To solve these problems, Python introduces the super() mechanism

super() and in python__ init__ () function of the method:

  • init() method is used to create the instance variable of the object;
  • The super() method is used to call the method of the parent class.

Example code:

class A:
    def __init__(self):
        print("A")


class B(A):
    def __init__(self):
        print("B")
        super().__init__()

Operation results:

B
A

**super() * * often occurs with multiple inheritance:

When multiple inheritance occurs, the order of inheritance is involved
 Calling the parent class method directly with the class name will involve problems such as search order and repeated calls.
super()Returns the next class in the inheritance order, not the parent class.

super() is very commonly used in class inheritance. It solves some problems of subclasses calling parent methods. When the parent class is called many times, it is only executed once, which optimizes the execution logic

2. Since we rewrite the constructor of B, why call super?

A: not all subclasses need to call super.

Assuming that Thread is inherited, name needs to be assigned in the initialization function. But do you really need an assignment?

In fact, the initialization function of Thread class itself makes a lot of judgments, so there is no need to write redundant code.

Therefore, the constructor is given to the parent class Thread to instantiate, and there is no need to write unnecessary logic.

Example code:

from threading import Thread


class MyThread(Thread):
    def __init__(self, name, user):
        self.user = user
        # Reuse parent code to some extent
        super().__init__(name=name)
       

3. What is the execution order of super?

Instead of calling the parent class directly, the super() function calls the constructor of the next class in the mro sequence

The calling order of super() function is not simply calling the parent class, but according to the calling order specified by mro() function

How is MRO called in the specified order?

The answer is based on python's built-in C3 algorithm

Example code:

class A:
    def __init__(self):
        print("A")


class B(A):
    def __init__(self):
        print("B")
        super().__init__()


class C(A):
    def __init__(self):
        print("C")
        super().__init__()


class D(B, C):
    def __init__(self):
        print("D")
        super().__init__()


if __name__ == "__main__":
    print(D.__mro__)
    d = D()

Operation results:

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
D
B
C
A

10, mixin inheritance cases (django, rest, framework)

Mixin

Mixin programming is a development mode, which is a way to combine the functional units in multiple classes. It sounds like it can be realized with class inheritance mechanism. However, it is different from traditional class inheritance.

Generally, mixin is not used as the base class of any class, nor does it care what class it is used with. Instead, it is dynamically combined with other scattered classes at runtime.

Recommendations:

  • In actual coding, try to use single inheritance instead of multiple inheritance to avoid confusion of inheritance
  • If there are multiple functions, write multiple classes
  • For future coding, try to use mixin mode

mixins multi inheritance cases (such as mixin s in django restframework)

  • 1.mixin has single function;
  • 2. If it is not associated with the base class, it can be combined with any base class. If the base class is not associated with mixin, it can be initialized successfully;
  • 3. Do not use super function in mixin;
  • 4. Try to end with Mixin

Example code:

'''
python about mixin The naming method is generally based on MixIn, able, ible Is a suffix.

because mixin It is composition, so it is addition to add new functions to existing classes, rather than inheritance,The next level overrides the same properties or methods as the previous level.
But it still behaves the same as inheritance in some aspects. For example, the instance of a class is also each instance mixin Examples of.
mixin Improper use will lead to class namespace pollution, so try to avoid it mixin The same method is defined in.
For the same method, it is sometimes difficult to distinguish which method is used by the instance.
'''
class Mixin1(object):
    def test(self):
        print("mixin 1")
    def which_test(self):
        self.test()


class Mixin2(object):
    def test(self):
        print("mixin 2")


class MyClass1(Mixin1, Mixin2):
    pass                        # Get functions from mixin from left to right and add them to MyClass


class Myclass2(Mixin1, Mixin2):
    def test(self):             # There are test methods, so the test methods of mixin1 and mixin2 will not be added
        print("my class 2")


c1 = MyClass1()
c1.test()                       #  "mixin 1"
c2 = MyClass2()
c2.test()                       #  "my class 2"
c2.which_test()                 #  "my class 2"
isinstance(c1, Mixin1)          #  True
issubclass(MyClass1, Mixin2)    #  True

11, with statement in Python

Content:

  • with and try except finally
  • with and context manager protocol

1. Function of with statement

What is a with statement

The with statement is a function related to exception handling introduced from Python 2.6. The with statement is applicable to the occasion of accessing resources to ensure that the necessary "cleaning" operation will be performed to release resources regardless of whether there are exceptions during use, such as automatic closing of files after use, automatic acquisition and release of locks in threads, etc.

The with statement is similar

try :
    
except:
    
finally:
    

Then why is there a problem that can be solved with try except finally? Why use the with statement?

  • Python is a short and concise language, which advocates a concise coding style, which can also be understood as python.
  • with uses a context manager, which can automatically obtain context related content, allowing developers to focus more on business.

(the with statement is more concise. And more secure. Less code.)

The with statement is a new control flow structure

with usage:

Its basic structure (format) is:

with expression [as variable]:
    with-block

A good example is file processing

You need to get a file handle, read data from the file, and then close the file handle. If the with statement is not used, the code is as follows:

file = open("foo.txt")
data = file.read()
file.close()

Here are two questions:

  • You may forget to close the file handle
  • An exception occurred while reading data from the file. No processing was performed

The following is the code with exception handling added at:

file = open("foo.txt")
try:
    data = file.read()
finally:
    file.close()

Although this code works well, it's too lengthy.

This is the time to show your skill.

In addition to having a more elegant syntax, with can also handle exceptions generated by the context well.

The following is the code of the with version:

with open("foo.txt") as file:
    data = file.read()

2. Context manager

It is a protocol in python. Instances of classes that implement this protocol are context manager objects.

So how can we achieve the agreement?

python is very simple. You only need to implement two methods when defining a class.
One is enter and the other is exit.

Example code:

class A():
    def __init__(self, val_a):
        self.a = val_a

    def __enter__(self):
        print("class A's __enter__ function.'")

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("class A's __exit__ function.")
        

In this way, an object that implements the context manager protocol is created. You can apply it to any class.

Python There are other advanced features in that can also create context managers, such as using decorators@contextmanager. 

contextmanager Decoration is defined in contextlib modular

Used@contextmanager Methods decorated by decorators will become context manager objects.



python Provided contextlib Module, omitting writing__enter__and__exit__Repeat the work. contextlib The module provides three objects: contextmanager Decorator, context manager closing and nested Function.


Share a big man's blog:

Deep understanding of Python with statements – punctuation (biaodianfu.com)****

12, contextlib simplified context manager

Python also provides a decorator of contextmanager in the contextlib module, which further simplifies the implementation of context manager.

Equivalent to simplification__ enter__ And__ exit__:

@contextlib. The contextmanager decorator will__ enter__ And__ exit__ Together and carried out a series of operations

The function is divided into two parts by yield. The statements before yield are executed in the enter method, and the statements after yield are executed in the exit method. The value immediately following yield is the return value of the function.

from contextlib import contextmanager

@contextmanager
def file_manager(name, mode):
    try:
        f = open(name, mode)
        yield f
    finally:
        f.close()
        
with file_manager('test.txt', 'w') as f:
    f.write('hello world')

In this code, the function file_manager() is a generator. When we execute the with statement, we will open the file and return the file object f; After the with statement is executed, the close file operation in the finally block will be executed. You can see that when using the generator based context manager, we will no longer define the "enter()" and "exit()" methods, but please be sure to add the decorator @ contextmanager, which is easy for novices to overlook.

Another big man's answer:

The function of the contextlib module is to provide a more easy-to-use context manager, which is implemented through the Generator.

The contextmanager in contextlib acts as a decorator to provide a context management mechanism at the function level.

Common frameworks are as follows:

from contextlib import contextmanager
@contextmanager

def make_context():
    print 'enter'
    try:
        yield "ok"
    except RuntimeError,err:
        print 'error',err
    finally:
        print 'exit'
         
>>>with make_context() as value:
    print value
     
Output is:
    enter
    ok
    exit

Among them, yield is written into try finally to ensure exception safety (can handle exceptions). The value of the variable after as is returned by yield.

The statement before yield can be regarded as the operation before the code block is executed, and the operation after yield can be regarded as the operation after yield__ exit__ Operation in function.

Take thread lock as an example:

@contextlib.contextmanager
def loudLock():
    print 'Locking'
    lock.acquire()
    yield
    print 'Releasing'
    lock.release()
 
with loudLock():
    print 'Lock is locked: %s' % lock.locked()
    print 'Doing something that needs locking'
 
#Output:
#Locking
#Lock is locked: True
#Doing something that needs locking
#Releasing

Keywords: Python Class Polymorphism

Added by poison6feet on Sat, 05 Feb 2022 20:36:28 +0200