33.Python面向对象类的专有方法__iter____getitem____getattr____call____new____init__

Posted 孤寒者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了33.Python面向对象类的专有方法__iter____getitem____getattr____call____new____init__相关的知识,希望对你有一定的参考价值。

目录:

每篇前言:


Python面向对象(六)

1.1 类的专有方法

__init__ : 构造函数,在生成对象时调用
__del__ : 析构函数,释放对象时使用
__repr__ : 打印,转换
__str__( self ):用于将值转化为适于人阅读的形式
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__len__: 获得长度
__call__: 函数调用
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方

基础重写方法:

# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
import math

class Vector:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __str__(self):
        return 'Vector (%s, %s)' % (self.a, self.b)

    def __repr__(self):
        return self.__str__()

    def __add__(self, other):
        return Vector(self.a + other.a, self.b + other.b)
    
    def __sub__(self, other):
        return Vector(self.a - other.a, self.b - other.b)
    
    def __mul__(self, other):
        return Vector(self.a * other.a, self.b * other.b)
    
    def __truediv__(self, other):
        return Vector(self.a / other.a, self.b / other.b)
    
    def __mod__(self, other):
        return Vector(self.a % other.a, self.b % other.b)
    
    def __pow__(self, other):
        return Vector(self.a ** other.a, self.b ** other.b)


v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)
print(v1 - v2)
print(v1 * v2)
print(v1 / v2)
print(v1 % v2)
print(v1 ** v2)

sorted_key = lambda v: math.sqrt(v.a ** 2 + v.b ** 2)
l = sorted([Vector(2, 10), Vector(3.5, 10), Vector(5, 8), Vector(4, 3)], key=sorted_key)
print(l)

1.1.1 __iter__

  • 如果一个类想被用于循环for…in,类似list或tuple那样,就必须实现一个方法__iter__(),该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的 next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
  • 我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
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  # 返回下一个值


for n in Fib():
    print(n, end=",")

1.1.2 __getitem__

  • 要表现得像list那样按照下标取出元素,需要实现方法:__getitem__()
# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
  • 现在,就可以按下标访问数列的任意一项了。
  • 对于切片方法[5:10],将传入slice对象,所以要做判断:
# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice):
            start = n.start
            stop = n.stop
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
  • 这个例子没有对step参数和负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。

1.1.3 __getattr__

  • 正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。
  • 要避免这个错误,除了可以加上一个相应属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:
# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
  • 当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, ‘score’)来尝试获得属性。
  • 返回函数也完全可以:
class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
  • 注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性不会在__getattr__中查找。

  • 链式调用示例:

# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
class Chain(object):
    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        if path == 'users':
            return lambda user: Chain('%s/users/%s' % (self._path, user))
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

print(Chain().status.user.timeline.list)
print(Chain().status.users('xiaoxiaoming').repos)

1.1.4 __call__实例方法

# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)
        
s = Student('Michael')
s()

  • __call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。
  • 如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。
  • 通过callable()函数可以判断一个对象是否是“可调用”对象:
>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('string')
False

1.1.5 __new__和__init__方法

__new____init__的作用:

  • __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供。__new__必须要有返回值,返回实例化出来的实例。
  • __init__有一个参数self,就是这个__new__返回的实例,__init____new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值。

实战讲解:

# -*- coding: utf-8 -*-
"""
__author__ = 小小明-代码实体
"""
class A(object):
    def __init__(self):
        print("__init__内部:", self, id(self))

    def __new__(cls):
        print("__new__内部:", cls, id(cls))
        obj = object.__new__(cls)
        print("__new__方法返回值:", obj, id(obj))
        return obj


a = A()
print("主程序:", a, id(a))
print("-----------")
b = A()
print("主程序:", b, id(b))

  • 可以看出,每次创建对象,都会先调用__new__方法,构建出对象,然后调用__init__方法,并将刚才创建的对象传入。

拓展:Linux中文本模式下怎么运行我们的python文件?

知识点补给站:

关于虚拟环境有篇文章有详细的介绍——《如何管理你下载的一大堆Python包【❤️win环境及linux环境下创建虚拟环境详解❤️】》

以上是关于33.Python面向对象类的专有方法__iter____getitem____getattr____call____new____init__的主要内容,如果未能解决你的问题,请参考以下文章

33.Python面向对象类的专有方法__iter____getitem____getattr____call____new____init__

面向对象之内置方法

Python_面向对象

Python3面向i对象编程实例

14.12.3类的特殊成员3

迭代器和生成器,面向过程编程