Python advanced programming lecture 1: in-depth classes and objects

1. Duck type and polymorphism

The concept of polymorphism is applied to strongly typed languages ​​like Java and C#, while Python advocates "duck type"

The so-called polymorphism: when the type at definition is different from the type at runtime, it becomes polymorphic. Maybe everyone is not easy to understand, that is, I don't know who to call when it is defined. Only when it is running can I know who to call.
Let's look at a piece of code:

class Cat(object):
#定义cat类型
    def info(self):
        print("I am Cat")
    

class Dog(object):
    #定义dog类
    def info(self):
        print("I am Dog")
        
class Duck(object):
    #定义duck类
    def info(self):
        print("I am Duck")
        
#将这些类放进一个列表中
animal_list = [Cat,Dog,Duck]

#遍历列表,我们的想法是此时这样调用应该是可以直接调用相应类里的info方法
for animal in animal_list:
    animal.info()

执行结果:
Traceback (most recent call last):
File “”, line 1, in
File “D:\PyCharm 2019.1\helpers\pydev_pydev_bundle\pydev_umd.py”, line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File “D:\PyCharm 2019.1\helpers\pydev_pydev_imps_pydev_execfile.py”, line 18, in execfile
exec(compile(contents+"\n", file, ‘exec’), glob, loc)
File “G:/pythonlianxi/spython/logic/python高级/第一讲/demo1.py”, line 23, in
animal.info()
TypeError: info() missing 1 required positional argument: ‘self’

Our idea in the above code is that through this method we can directly output the results we want, but we ignore a very important point, that is, we based on the usual instantiation of the class, in the above code, we did not perform the class An error occurred during instantiation. The
correct code should be written as follows:

class Cat(object):
#定义cat类型
    def info(self):
        print("I am Cat")
    

class Dog(object):
    #定义dog类
    def info(self):
        print("I am Dog")
        
class Duck(object):
    #定义duck类
    def info(self):
        print("I am Duck")
        
#将这些类放进一个列表中
animal_list = [Cat,Dog,Duck]

#遍历列表,我们的想法是此时这样调用应该是可以直接调用相应类里的info方法
for animal in animal_list:
    #将类实例化,通过类名+()的这种形式
    animal().info()

Execution result:
I am Cat
I am Dog
I am Duck
At this point we can know that if we want to call the method of the class, we must first instantiate the class

2. Abstract base class (abc module)

  • Abstract base class (abstract base class, ABC): An abstract base class is a class in which pure virtual member functions are defined. Pure virtual functions only provide an interface, but no specific implementation. An abstract base class cannot be instantiated (objects cannot be created), and is usually used as a base class for subclasses to inherit. The subclasses override virtual functions to implement specific interfaces.
  • An abstract base class is a class that defines various methods without specific implementation. Any class that inherits from an abstract base class must implement these methods, otherwise it cannot be instantiated.

2.1 Abstract base class application scenarios

  • 1. Let's check whether a certain class has a certain method
    Sample code:
#检查某个类是否有某种方法
class Demo(object):
    def __init__(self,my_list):
        self.my_list = my_list
    def __len__(self):
        return len(self.my_list)  
d=Demo(['hello','world'])

from collections.abc import Sized
print(isinstance(d,Sized))

The execution result is: True
Sized is implemented in a hidden file, including the __len()__ method, so we will be True in the final judgment

  • 2. We need to force a certain subclass to implement certain methods
  • 1Here we simulate the forced implementation scenario:
    Code 1:
#需要某个子类必须实现的方法
#此处为模拟实现
class CacheBase(object):
    def get(self,key):
        raise ValueError
    
    def set(self,key,value):
        raise InterruptedError
   
class RedisCache(CacheBase):
    pass
r = RedisCache()
r.get('zjk')

Execution result: At this time, the program will throw an exception directly, because the method in the parent class is not rewritten during instantiation

We re-adjust the program, the code is as follows:
Code 2:

class CacheBase(object):
    def get(self,key):
        raise ValueError
    
    def set(self,key,value):
        raise InterruptedError
   
class RedisCache(CacheBase):
    def get(self,key):
        pass

r = RedisCache()
r.get('zjk')

At this time, the program is running normally and no error is reported, but because it is simulated, it is not accurate. This method is not recommended

  • 2 correct writing

Code 1 does not rewrite the get and set methods during instantiation, as follows:

import abc
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass
    
    @abc.abstractmethod
    def set(self,key,value):
        pass
   
class RedisCache(CacheBase):
    pass

r = RedisCache()
r.get('zjk')

When the program is executed at this time, an error will be reported, saying that there are no corresponding get and set methods

So we have to rewrite the corresponding method in the example. The
code is as follows:

import abc
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self,key):
        pass
    
    @abc.abstractmethod
    def set(self,key,value):
        pass
    
class RedisCache(CacheBase):
    def get(self,key):
        pass
    def set(self,key,value):
        pass

r = RedisCache()
r.get('zjk')

3. The difference between isinstance and type

Conventional difference
    1. .isinstance function to determine whether an object is a known type
s='123'
print(isinstance(s,str))

The execution result is: True

  • 2.type tells us the type directly
s='123'
print(type(s))

The execution result is: <class'str'>

The difference in object orientation
  • type does not consider inheritance
  • instance consider inheritance

In object-oriented programming, use isinstance to avoid misjudgment

Is the reference is the same object == only judges whether the values ​​are equal

4. Class attributes and instance attributes

First look at the following code:

class A:
#类属性    
bb = 1
    
    def __init__(self,x,y):
        #实例属性
        self.x= x
        self.y= y
        
a = A(1,2)
A.bb=11
a.bb=22  #此句的实际作用相当于在实例里增加了一个bb属性,和类的bb属性不是一样的
print(a.x,a.y,a.bb)

print(A.bb)

print(A.x)

Analyzing the output results, we conclude that the instance can look up the attributes of the class, but the class cannot look down the attributes of the attribute

5. Search order

After the mro algorithm in Python 2.3, Python adopted the C3 algorithm
DFS

BFS

6. The introspection mechanism of Python objects

Introspection is to query the internal structure of the object through a certain mechanism.
Introspection is the ability to know the type of object when a program written in an object-oriented language is running. In a nutshell, the type of object can be known at runtime.

For example: the dir function will list the corresponding magic methods and attributes, type, isinstance, hasattri

class Person:
    name = 'zjk'
    
class Student(Person):
    def __init__(self, name):
        self.name = name
        
if __name__ == '__main__':
    u = Student('zs')
    print(u.__dict__)
    print(u.name)
    
    u.__dict__['add']= 'HZ'
    print(u.__dict__)
    print(dir())

7.Super function

Why use the super function?

  • Because the method in the parent class is normally called, we can also hard-code this way through the class name + method name. Once the name of the parent class changes, then all the places we refer to have to be modified, and the code maintainability is more difference. And by the method of parent class name + method name, when calling its method, you need to pass in a self parameter when passing parameters.
  • The super(). method name is a soft-coded method to refer to the parent class. If the parent class name changes, the code behind us does not need to be updated, because super() will automatically parse the parent class information.
  • super() In the complex inheritance relationship, instead of calling the method in the parent class, it is called according to the mro algorithm
  • I want to call the method of the parent class in the instance method
class A:
    def __init__(self):
          print("A")
class B(A):
      def __init__(self):
      print('B')
      super().__init__()
if __name__ == "__main__":
b = B()
  • You can initialize the new class quickly by calling the existing init initialization method in the parent class in the instance method
class Person(object):
    def __init__(self, name, age, heigt):
        self.name = name
        self.age = age
        self.height = heigt
    
    def speak(self):
        print('{}说:我{}岁'.format(self.name,self.age))
    
class Student(Person):
    def __init__(self,name,age,height,grade):
        super().__init__(name,age,height)
        self.grade = grade
    def info(self):
        print('{}说:我{}岁,我在{}年级'.format(self.name,self.age,self.grade))
        
if __name__ == '__main__':
    u = Student('zs',18,116,5)
    u.info()

8. Class and object trial problems and solving skills

  • 1 How to derive a built-in immutable type to modify its instantiation behavior?

Scenario: I want to customize a new type of
ungroup . For the incoming iterable object, we only keep the elements of the int type and the value greater than 0. IntTuple([2,-2,'zs',['x' ,'y',4]))=>(2,4)
How to inherit built-in tuple to implement inttuple

'''
想自定义一种新类型的无组,对于传入的可迭代的对象,我们只保留其中int类型且值大于0的元素
IntTuple([2,-2,'zs',['x','y',4]])=>(2,4)
如何继承内置tuple实现inttuple
'''

#定义IntTuple类
class IntTuple(tuple):
    #定义初始化方法,并传入 可迭代的参数
    def __init__(self,iterable):
        f = (i for i in iterable if isinstance(i,int) and i>0)
        super().__init__(f)
        
        
result = IntTuple([2,-2,'zs',['x','y',4]])
print(result)

执行结果如下:
Traceback (most recent call last):
File “”, line 1, in
File “D:\PyCharm 2019.1\helpers\pydev_pydev_bundle\pydev_umd.py”, line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File “D:\PyCharm 2019.1\helpers\pydev_pydev_imps_pydev_execfile.py”, line 18, in execfile
exec(compile(contents+"\n", file, ‘exec’), glob, loc)
File “G:/pythonlianxi/spython/logic/python高级/第一讲/demo6.py”, line 15, in
result = IntTuple([2,-2,‘zs’,[‘x’,‘y’,4]])
File “G:/pythonlianxi/spython/logic/python高级/第一讲/demo6.py”, line 12, in init
super().init(f)
TypeError: object.init() takes no parameters
We can see that the program reported an error

Below we use several sets of codes to analyze which methods are available before some initialization methods of data types. The
first code:

class A:
    def __new__(cls, *args, **kwargs):
        print("A__new__",cls,args)
        return object.__new__(cls) #new方法需要返回
    def __init__(self,*args):
        print("A__init__",args)
        
a=A(1,2)

Execution result:
A__new__ <class' main .A'> (1, 2)
A__init__ (1, 2)
We can see that there is a new method before __init__

Let's transform the code a bit

class A:
    def __new__(cls, *args, **kwargs):
        print("A__new__",cls,args)
        return object.__new__(cls) #new方法需要返回
    def __init__(self,*args):
        print("A__init__",args)
        
#A.__new__(1,2)  #直接这样传会报错,因为在上面的代码中我们对类进行实例化,所以上面的代码不会报错,而此片因为没有对类进行实例化,所以 我们要遵守上面代码中一些规则,按规则传入相应的参数,所以要改为下面的写法
A.__new__(A,1,2)  #此时代码正常运行
A.__init__(A,1,2)

Let's take a look at which magic method the list executes first:
code:

ilist = list('abc')
print(ilist)
l = list.__new__(list,'abc')
print(l)

Execution result:
['a','b','c']
[]

ilist = list('abc')
print(ilist)
l = list.__new__(list,'abc')
list.__init__(l,'abc')
print(l)

Execution result:
['a','b','c']
['a','b','c']

We can see that the initialization of the list is not achieved through the new method, but through the init

Let's look at the tuple

ituple =tuple('abc')
print(ituple)
newtuple = tuple.__new__(tuple,'bcd')
print(newtuple)

Execution result:
('a','b','c')
('b','c','d')
We can see that it can be output directly. From this we can see that the tuple is completed by new, not by Init to achieve.

So we modify the original code as follows:

# #定义IntTuple类
class IntTuple(tuple):
    #定义初始化方法,并传入 可迭代的参数
    def __new__(cls,iterable):
        f = (i for i in iterable if isinstance(i,int) and i>0)
        return super().__new__(cls,f)


result = IntTuple([2,-2,'zs',['x','y'],4])
print(result)

At this point, the function we want has been completed! !

Finally, thank you all for reading! !

Guess you like

Origin blog.csdn.net/scyllake/article/details/96306921