Object-Oriented Programming for Getting Started with Python (3) Magic Methods of Python Classes

There are some methods in the python class that have two underscores before and after, such functions are collectively called magic methods. These methods have special purposes, some do not need to be defined by ourselves, and some can achieve more magical functions through some simple definitions

I mainly divide them into three parts, and the following is also divided into these three parts to explain

  • Simple, functional, generally do not modify the direct call to view the results
  • Implement simple functions, define and use
  • The realization of more complex functions is often the realization principle of some of the characteristics of our common classes, which are divided into
    • Instances become iterables (a property of lists)
    • Instance index value (property of list)
    • Context management (properties of files), etc.

 

Simple function

This section includes the first two points listed above

There is no need to modify the direct call, mainly as follows

  • Both __dict__ classes and instances can call this method
    • A class call returns the properties and methods already defined in this class, including special methods
    • An instance call returns a dictionary of properties
  • __module__ Both classes and instances can be called. The module where the class is located, such as the bb.py file in the aa folder, returns aa.bb
  • __class__ Only the instance can be called, indicating which class the instance belongs to, and the content includes the information of __module__
  • The dir() function acts on classes and instances, returning all its attributes and methods, which is actually equivalent to calling the __dir__() function

Generally used after a simple definition

  • __doc__ Returns the string marked when the class is defined. The marked position must be before the defined method attribute. The default is None
  • __slots__ = ('name', 'age') Add a sentence that allows only these two attributes to be defined and cannot be added to the instance. This command only works on the current class, not on subclasses
  • __init__ is used to define instance attributes
  • __call__ Input instance() or class()() trigger, only if this is defined, the instance can be followed by parentheses like a function
  • The content printed when __str__ prints an instance, it can be called if it is not defined, and the printing content can be customized after redefining
  • __repr__ directly outputs the content printed by the instance name. It can be called if it is not defined. After redefining it, the printing content can be customized
  • __new__ automatically calls __del__ when an instance is created Triggered when the object is released in memory (the example will not be set later)
  • __len__ defines the content returned by len (instance), for example, the string class defines the number of characters in the string, of course, it can be changed to other
  • __eq__ changes the behavior of the == operator. When defining an instance using ==number, what is the instance compared to number?
  • Similar to eq is ne lt le gt ge
  • There are also some methods such as __add__ __abs__ for numerical calculation between instances, which are defined in a similar way to __eq__. The typical class of these applications is the numerical class. Defining these actually defines the behavior of certain operators, such as __iadd_ _ defines += to behave the same. In addition, there are conversion to integer, floating point,
  • __getattr__ When the attribute accessed by the instance is not defined, an error will be reported. After this is defined, it will be output according to the definition here. setattr can set the attribute delattr and delete the attribute

The following is a demonstration of the use of the above method

class Special:
     ''' Describe the information of the class, __doc__ returns the string result placed here '''
    
    __slots__ = ( ' name ' , ' age ' , ' __weight ' ) #limit the value of the attribute
    
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.__weight = weight
    
    def  __call__ (self, content): #define what the instance() will return 
        print (content)
        
    def  __str__ (self): #define what result will be returned by printing instance return ' Special object (name:%s) ' % self.name
         
    
    __repr__ = __str__ #This   is a simple assignment
    
    def  __len__ (self): #Define the result returned by the len function return len(self.name )
        
    
    def  __eq__ (self, num): #Instance and number ratio 
        return len(self.name) == num
    
    def  __gt__ (self, other): #This can achieve instance and instance ratio 
        return len(self.name) >= len(other)
    
    def __getattr__(self, attr):
        if attr=='score':
            return "You can't see the score"

Create instance

s = Special("Bob", 5, 4)
# s.hobby = "running" # 报错,因为__slots__限制了属性取值
s.__doc__ # 返回类定义时下方写的字符串描述内容
# '描述类的信息,__doc__返回放在这里的字符串结果'
s("__call__ is used") # 调用了__call__
# __call__ is used
print(s) # 调用__str__
# Special object (name:Bob)
s # 调用__repr__
# Special object (name:Bob)
len(s) # 调用了__len__
# 3
s==3 # 调用了__eq__
# True
Special("Mary", 4,5) > Special("Bob",5,4) # 调用__gt__
# True
s.score # 无score参数,调用__getattr__
# "You can't see the score"

s.__dict__ # 要把__slots__注释掉才能看见属性的字典
# {'_Special__weight': 4, 'age': 5, 'name': 'Bob'}
s.__module__ # 查看所属模块
# '__main__'
s.__class__ # 查看所属类
# __main__.Special
dir(s) # 返回实例的所有属性和方法,调用了__dir__()方法

 

可迭代对象

使实例成为可迭代对象(可以被for循环的)

  • 实现 __iter__ __next__ 方法
  • 如果只实现__iter__则这个方法的返回值必须是一个迭代器
  • 也可以__iter__返回一个self,再定义__next__方法,接受self为参数,在里面具体实现如何取得下一个值以及迭代器何时结束
  • 原理是当对实例调用for循环时,相当于每次对__iter__的返回结果作用一次next()函数,所以要想迭代必须定义__iter__方法。第一种就是next每次正常调用__iter__返回的值,这就要求它的返回值是一个迭代器;第二种则是把next函数改掉,使其功能不再是找到下一个,而是定制我们想要的一些操作

第一种

class Ite1:
    
    def __init__(self, a):
        self.a = a
        
    def __iter__(self):
        return iter(range(2*self.a)) # iter函数将一个可迭代对象变成迭代器
     
i1 = Ite1(3)
for i in i1:
    print(i) # 0到 5

第二种

class Ite2:
    
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
    def __iter__(self):
        return self
    
    def __next__(self):
        self.a += 1
        if self.a > self.b+1: # 条件成立则迭代器结束
            raise StopIteration()
        return self.a-1
    
for i in Ite2(2,5):
    print(i) # 返回2-5

 

索引取值

这里实现使用中括号索引取值,或者像字典一样操作

  • 实现 __getitem__方法
  • 这个方法的参数除了self,还可以指定一个index,之后return一个和index相关的结果,其实相当于把实例定义成了一个函数,但是是用中括号调用的
  • 结合 __setitem__ __delitem__ 即可让实例像字典一样操作

只定义__getitem__

class Index1:
    
    def __getitem__(self, index):
        return 2*index # 如果定义和__next__中内容类似则实现既可以循环又可以[]取值了
    
i = Index1()
i[2] # 4

全部定义

class Index2:
    
    def __init__(self,**kw):
        self.dict = kw 
            
    def __getitem__(self, key):
        return self.dict[key]
    
    def __setitem__(self, key, value):
        self.dict[key] = value
        
    def __delitem__(self, key):
        del self.dict[key]
        
i = Index2(name="Bob")
i['name'] # 'Bob'
i['age'] = 13
i['age'] # 13
del i['age']
# i['age'] #报错,删掉就没有这个属性了

 

上下文管理

实现上下文管理,即可以和with结合使用

  • 要实现 __enter__ __exit__ 两个方法
  • __enter__会返回一个值,并赋值给as关键词之后的变量
  • __exit__ 定义了处理结束后要做的事情,比如文件的关闭,socket的断开等
  • 更深入地使用:__exit__中可以处理异常。
    • 在上下文管理中运行的代码如果报错,会将三个值自动传入__exit__方法中,分别为 异常的类型,异常的值,异常的追踪栈
    • 通过定义__exit__的返回值可以进行不同的处理,共有两种返回形式,返回True则这个异常忽略,返回None则正常抛出异常

简单实现上下文管理

class Manager:
    
    def __init__(self, text):
        self.text = text
    
    def __enter__(self):
        self.text = "It has started " + self.text
        return self # 还是让这个类的实例进行下面的处理
        
    def __exit__(self, ex_type, ex_value, ex_tb):
        self.text = "It is over"
        
        
with Manager("machine") as m:
    print(m.text)
    
print(m.text) # as定义的m仍然可以调用
# It has started machine
# It is over
# 发现最后执行了__exit__ 把这个属性改变了

异常处理

class DemoManager:

    def __enter__(self):
        pass

    def __exit__(self, ex_type, ex_value, ex_tb):
        if ex_type is IndexError:
            print(ex_value.__class__)
            return True
        if ex_type is TypeError:
            print(ex_value.__class__)
            return  # return None

# 下面故意制造两种错误
with DemoManager() as nothing:
    data = [1, 2, 3]
    data[4]  # raise IndexError, 该异常被__exit__处理了

with DemoManager() as nothing:
    data = [1, 2, 3]
    data['a']  # raise TypeError, 该异常没有被__exit__处理

 

 

参考

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324928969&siteId=291194637