• Python中常用魔术方法


    阅读别人编写的Python代码时,经常会在他们定义的类中看到以“__”为开头和结尾的方法,经过进一步学习后才知道类中实现的这类方法被称为“魔术方法”。“魔术方法”在一些情况下会被自动的调用,通过一些简单的定义就可以实现比较神奇的功能。如果你希望根据自己的需求去实现具有“特殊”功能的类,那么就需要对这些方法进行重写。下面内容将对一些常用的“魔术方法”进行介绍。

    1、__init__方法

    __init__方法功能有点类似于其它面向对象语言中的构造函数,在类实例化对象时它会被自动的调用。一般情况下,在定义的类中都会实现__init__方法,在__init__方法内根据创建类对象时传入参数对类对象的属性进行初始化操作。

    class student(object):
        def __init__(self, _name, _age):
            self.name = _name
            self.age  = _age
            
        def get_info(self):
            print('%s is %d years old' %(self.name, self.age))

    测试结果:

    >>> stu = student("Michael", 12)
    >>> stu.get_info()
    Michael is 12 years old

    2、__new__方法

    前面的__init__方法承担了类实例化对象后对类对象属性必要的初始化操作,而__new__方法则是在__init__方法之前被调用创建类对象。与__init__方法不同的是__new__方法必须返回一个值,返回所创建对象的实例。

    __new__方法定义形式:

    def __new__(cls, *args, **kw):
        pass

    __new__方法的首个参数是cls,因此,它是属于一个类方法,这也是我们可以通过object.__new__来调用它的原因。

    不过,一般情况下,在定义的类中很少去重写__new__方法,但在实现单例设计模式时,可通过重写__new__方法实现。

    class single(object):
        _instance = None
    
        def __new__(cls, *args, **kwargs):
            if cls._instance is None:
                cls._instance = super(single, cls).__new__(cls, *args, **kwargs)
            return cls._instance
            
        def __init(self):
            pass

    上述single类,通过重写了__new__方法,实现了单例设计模式。_instance是single类的属性,用于描述是否已经创建了实例对象。在创建一个single对象时,首先根据类属性_instance判断是否是第一次创建对象,如果是调用父类的__new__方法创建对象,如果不是,就返回之前创建的对象。

    测试结果:

    >>> obj1 = single()
    >>> obj2 = single()
    >>> obj1
    <__main__.single object at 0x0000000003D44F08>
    >>> obj2
    <__main__.single object at 0x0000000003D44F08>

    3、__call__方法

    该方法的功能类似于在类中重载()运算符,使得类实例对象可以像调用普通函数那样,以“类对象名()”形式使用,调用的结果即调用到__call__方法。

    class call_cls(object):
        def __call__(self):
            print('__call__ function...')

    测试结果:

    >>> obj = call_cls()
    >>> obj()
    __call__ function...

    4、__str__方法

    如果类中实现了__str__方法,当使用print输出对象时,那么就会打印出从这个方法中返回的数据

    class cat(object):
        def __init__(self, _name, _hobby):
            self.name  = _name
            self.hobby = _hobby
            
        def __str__(self):
            return ('%s hobby is %s' %(self.name, self.hobby))

    测试结果:

    >>> Tom = Cat('Tom', 'eat fish')
    >>> print(Tom)
    Tom hobby is eat fish

    5、__iter__和__next__方法

    如果一个类想像list、tuple那样,可以用于for...in循环,那么它就要在内部实现一个__iter__方法,该方法返回一个迭代对象。然后Python的for循环不断的调用该迭代对象的__next__方法来获取循环的下一个值,直到遇到StopIteration异常时退出循环。

    现在我们来写一个可用于for...in循环的类,这个类用来计算斐波那契数列

    先来看第一种写法:

    class Fib_Calc(object):
        def __init__(self):
            self.a, self.b = 0, 1 # 初始化两个计数器a,b
    
        def __next__(self):
            self.a, self.b = self.b, self.a + self.b # 计算下一个值
            if self.a > 100: # 退出循环的条件
                raise StopIteration()
            return self.a # 返回下一个值        
            
    class Fib(object):
        def __iter__(self):
            return Fib_Calc()

    Fib类中实现__iter__方法,该方法中要返回一个可迭代对象。该对象是Fib_Calc类的实例化,所以,Fib_Calc中还应包含__next__方法

    Fib_Calc类中的__next__方法根据斐波那契数列特点,计算一次结果

    测试结果:

    >>> fib = Fib()
    >>> for i in fib:
        print(i)
    
        
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    89

    再来看第二种写法:

    class Fib(object):
        def __init__(self):
            self.a, self.b = 0, 1 # 初始化两个计数器a,b
    
        def __iter__(self):
            return self # 实例本身就是迭代对象,故返回自己
    
        def __next__(self):
            self.a, self.b = self.b, self.a + self.b # 计算下一个值
            if self.a > 100000: # 退出循环的条件
                raise StopIteration()
            return self.a # 返回下一个值

    我们可以不像第一种写法那样,直接将__iter__和__next__方法在同一个类中实现。一个类包含__next__方法,这个类就是一个可迭代类,在__iter__方法中只需要返回这个对象自身,那么返回的结果就是一个可迭代对象。

    这里同时实现__iter__和__next__方法的对象,也就是我们所说的“迭代器”。

    6、__getitem__方法

    上面使用__iter__和__next__方法,实现了类能像list和tuple一样使用for...in循环,那么类是否能像list那样按下标取出元素呢?

    当然是可以的,如果类要表现得像list那样按照下标取出元素,需要在类中实现__getitem__方法。仍然以斐波拉契数列为例进行演示

    class Fib(object):
        def __getitem__(self, n):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a    

    现在,就可以按下标访问数列的任意一项了:

    >>> fib = Fib()
    >>> fib[2]
    2
    >>> fib[4]
    5

    已经实现了下标取数据,但是要像list一样使用切片功能,还需要在__getitem__方法中加入判断。

    class Fib(object):
        def __getitem__(self, n):
            if isinstance(n, int): # n是索引
                a, b = 1, 1
                for x in range(n):
                    a, b = b, a + b
                return a
            if isinstance(n, slice): # n是切片
                start = n.start
                stop = n.stop
                if start is None:
                    start = 0
                a, b = 1, 1
                L = []
                for x in range(stop):
                    if x >= start:
                        L.append(a)
                    a, b = b, a + b
                return L

    __getitem__()中检测到以切片方式访问时,获取切片开始和结束位置,计算开始至结束位置范围对应的斐波拉契数组值,并将其放入到一个列表中,待循环结束后返回这个列表。

    测试结果:

    >>> fib = Fib()
    >>> fib[4]
    5
    >>> fib[:5]
    [1, 1, 2, 3, 5]

    7、__getattr__方法

    一般情况下,如果我们访问到一个对象中不存在的成员属性时,会发生异常

    def student(object):
        def __init__(self, _name):
            self.name = _name
    >>> stu = student('Li Ming')
    >>> print(stu.name)
    Li Ming
    >>> print(stu.age)
    Traceback (most recent call last):
      File "<pyshell#19>", line 1, in <module>
        print(stu.age)
    AttributeError: 'student' object has no attribute 'age'

    很明显,这是因为类中没有age这个属性引起的。除了在类中添加age属性外,Python中还提供了另外一种机制,在类实现__getattr__方法,动态返回一个属性。

    >>> stu = student('Li Ming')
    >>> print(stu.age)
    100

    小结

    这里仅介绍的最常用的几个“魔术方法”,通过例子演示,我们可以看到Python编程时使用“魔术方法”去定制自己类的优势所在。Python中还有很多“魔术方法”可供我们去使用,后续接触到时再对本文的内容进行补充!

  • 相关阅读:
    nginx显示中文乱码
    Job for mariadb.service failed because the control process exited with error code. See "systemctl status mariadb.service" and "journalctl -xe" for details
    linux 7 网卡配置
    zabbix 离线安装
    linux alias 设置命令别名
    重启redis shell脚本
    docker离线安装
    ansible 批量添加用户
    linux 7 离线安装ansible
    Linux 7 配置163yum源
  • 原文地址:https://www.cnblogs.com/053179hu/p/14200551.html
Copyright © 2020-2023  润新知