DAY6第六次任务

魔法方法

一. 基本的魔法方法

二.算术运算符

三.反算术运算符

四.增量赋值运算符

五一元运算符

六.属性访问

七. 描述符

八.定制序列

九.迭代器

十.习题

十一.总结


一.基本的魔法对象

1.1__init__

__init__(self[, ...]) 在上一章类与对象中,有对__init__的应用,其主要作用是在类的实例化时,执行一些初始化程序。

class Person:
    def __init__(self,name,height,weight):
        self.name=name
        self.height=height
        self.weight=weight
    def intro(self):
        print('我叫%s,我身高%dcm,体重%dkg'%(self.name,self.height,self.weight))

who=Person('小王',180,75)
who.intro() #我叫小王,我身高180cm,体重75kg

在创建实例who的时候,__init__就会被调用,接收传进来的参数。值得注意的是,__init__不能有返回值,它的返回值只能是none,如果return别的东西会报错。

1.2__new__

本来以为初始化方法__init__是第一个执行的,没想到还有比它更早的,那就是__new__,全拼__new__(cls[, ...])
__new__只有第一个参数cls是自己的,其他的都要传给__init__。但是__new__不一定会进入__init__,只有它返回当前cls时才会进入,因为__new__可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例.

class One(object):
    def __init__(self, value):
        self.value = value

    def __new__(cls, *args, **kwargs):
        return Two(*args, **kwargs)  
        #因为这个return的存在,本来应该执行__init__的,结果跑去执行Two()了
    def ok(self):  #One里的方法
        print('%s,are you OK?' % (self.value))

class Two(object):
    def __init__(self, value):
        self.value = value

    def ok(self): #Two里的方法
        print('%s,how are you?' % (self.value))

who=One('小王')
who.ok() #小王,how are you?

上面这个例子中,__new__返回的是__Two()__,不是当前的cls,因此就没有执行__init__

1.2__del__

__del__是python中用来回收对象的方法,一个对象被几个变量引用就会计数为几,如果为0时可以用__del__回收。

class C(object):
    def __init__(self):
        print('into C __init__')

    def __del__(self):
        print('into C __del__')

c1 = C()
# into C __init__
c2 = c1
c3 = c2
del c3
del c2
del c1
# into C __del__

然后神奇的一幕发生了,用jupyter notebook和pycharm竟然出现了不同的结果!!!

在这里插入图片描述在这里插入图片描述

1.3__str__和__repr__

  • __str__(self):

    • 当你打印一个对象的时候,触发__str__
    • 当你使用%s格式化的时候,触发__str__
    • str强转数据类型的时候,触发__str__
  • __repr__(self)

    • reprstr的备胎
    • __str__的时候执行__str__,没有实现__str__的时候,执行__repr__
    • repr(obj)内置函数对应的结果是__repr__的返回值
    • 当你使用%r格式化的时候 触发__repr__
class Cat:
    """定义一个猫类"""

    def __init__(self, new_name, new_age):
        """在创建完对象之后 会自动调用, 它完成对象的初始化的功能"""
        self.name = new_name
        self.age = new_age

    def __str__(self):
        """返回一个对象的描述信息"""
        return "名字是:%s , 年龄是:%d" % (self.name, self.age)
        
    def __repr__(self):
        """返回一个对象的描述信息"""
        return "Cat:(%s,%d)" % (self.name, self.age)

    def eat(self):
        print("%s在吃鱼...." % self.name)

    def drink(self):
        print("%s在喝可乐..." % self.name)

    def introduce(self):
        print("名字是:%s, 年龄是:%d" % (self.name, self.age))


# 创建了一个对象
tom = Cat("汤姆", 30)
print(tom)  # 名字是:汤姆 , 年龄是:30
print(str(tom)) # 名字是:汤姆 , 年龄是:30
print(repr(tom))  # Cat:(汤姆,30)
tom.eat()  # 汤姆在吃鱼....
tom.introduce()  # 名字是:汤姆, 年龄是:30

二.算术运算符

算术运算符描述
__add__(self, other)定义加法的行为:+
__sub__(self, other)定义减法的行为:-
__mul__(self, other)定义乘法的行为:*
__truediv__(self, other)定义真除法的行为:/
__floordiv__(self, other)定义整数除法的行为://
__mod__(self, other)定义取模算法的行为:%
__divmod__(self, other)定义当被 divmod() 调用时的行为
divmod(a, b)把除数和余数运算结果结合起来,
返回一个包含商和余数的元组(a // b, a % b)
__pow__(self, other[, module])定义当被 power() 调用或 ** 运算时的行为
__lshift__(self, other)定义按位左移位的行为:<<
__rshift__(self, other)定义按位右移位的行为:>>
__and__(self, other)定义按位与操作的行为:&
__xor__(self, other)定义按位异或操作的行为:^
__or__(self, other)定义按位或操作的行为:|

三.反算术运算符

算术运算符描述
__radd__(self, other)定义加法的行为:+
__rsub__(self, other)定义减法的行为:-
__rmul__(self, other)定义乘法的行为:*
__rtruediv__(self, other)定义真除法的行为:/
__rfloordiv__(self, other)定义整数除法的行为://
__rmod__(self, other)定义取模算法的行为:%
__rdivmod__(self, other)定义当被 divmod() 调用时的行为
__rpow__(self, other[, module])定义当被 power() 调用或 ** 运算时的行为
__rlshift__(self, other)定义按位左移位的行为:<<
__rrshift__(self, other)定义按位右移位的行为:>>
__rand__(self, other)定义按位与操作的行为:&
__rxor__(self, other)定义按位异或操作的行为:^
__ror__(self, other)定义按位或操作的行为:|
class Nint(int):
    def __radd__(self, other):
        return int.__sub__(other, self) # 注意 self 在后面


a = Nint(5)
b = Nint(3)
print(a + b)  # 8
print(1 + b)  # -2

四.增量赋值运算符

算术运算符描述
__iadd__(self, other)定义赋值加法的行为:+=
__isub__(self, other)定义赋值减法的行为:-=
__imul__(self, other)定义赋值乘法的行为:*=
__itruediv__(self, other)定义赋值真除法的行为:/=
__ifloordiv__(self, other)定义赋值整数除法的行为://=
__imod__(self, other)定义赋值取模算法的行为:%=
__ipow__(self, other[, modulo])定义赋值幂运算的行为:**=
__ilshift__(self, other)定义赋值按位左移位的行为:<<=
__irshift__(self, other)定义赋值按位右移位的行为:>>=
__iand__(self, other)定义赋值按位与操作的行为:&=
__ixor__(self, other)定义赋值按位异或操作的行为:^=
__ior__(self, other)定义赋值按位或操作的行为:|=

五.一元运算符

算术运算符描述
__neg__(self)定义正号的行为:+x
__pos__(self)定义负号的行为:-x
__abs__(self)定义当被abs()调用时的行为
__invert__(self)定义按位求反的行为:~x

六.属性访问

算术运算符描述
__getattr__(self, name)定义当用户试图获取一个不存在的属性时的行为。
__getattribute__(self, name)定义当该类的属性被访问时的行为
(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__`)。
__setattr__(self, name, value)定义当一个属性被设置时的行为。
__delattr__(self, name)定义当一个属性被删除时的行为。

七. 描述符

算术运算符描述
__get__(self, instance, owner)用于访问属性,它返回属性的值。
__set__(self, instance, value)将在属性分配操作中调用,不返回任何内容。
__del__(self, instance)控制删除操作,不返回任何内容。

八.定制序列

协议(Protocols)与其它编程语言中的接口很相似,它规定你哪些方法必须要定义。然而,在 Python 中的协议就显得不那么正式。事实上,在 Python 中,协议更像是一种指南。

容器类型的协议

  • 如果说你希望定制的容器是不可变的话,你只需要定义__len__()__getitem__()方法。
  • 如果你希望定制的容器是可变的话,除了__len__()__getitem__()方法,你还需要定义__setitem__()__delitem__()两个方法。

【例子】编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。

class CountList:
    def __init__(self, *args):
        self.values = [x for x in args]
        self.count = {}.fromkeys(range(len(self.values)), 0)

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

    def __getitem__(self, item):
        self.count[item] += 1
        return self.values[item]


c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1])  # 3
print(c2[2])  # 6
print(c1[1] + c2[1])  # 7

print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}

print(c2.count)

九.迭代器

迭代器是访问集合元素的一种方式,能够记住遍历的位置的对象,从第一个元素开始访问,直到所有的元素访问完毕,只能往前不能后退。
很常用的迭代器是for in

a = [1,2,3,4,5]
it=iter(a)

print(next(it)) #1
print(next(it)) #2
print(next(it)) #3
print(next(it)) #4
print(next(it)) #5
print(next(it)) #StopIteration

把一个类作为一个迭代器使用需要在类中实现两个魔法方法 __iter__()__next__()

  • __iter__(self)定义当迭代容器中的元素的行为,返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
  • __next__() 返回下一个迭代器对象。
  • StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
class Fibs:
    def __init__(self, n=10):
        self.a = 0
        self.b = 1
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > self.n:
            raise StopIteration
        return self.a


fibs = Fibs(100)
for each in fibs:
    print(each, end=' ')

# 1 1 2 3 5 8 13 21 34 55 89

十.生成器

在python中,使用yield的函数被称为生成器,生成器生成的函数只能用于迭代操作,在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

def myGen():
    print('生成器执行!')
    yield 1
    yield 2


myG = myGen()
print(next(myG))  
# 生成器执行!
# 1

print(next(myG))  # 2
print(next(myG))  # StopIteration

myG = myGen()
for each in myG:
    print(each)

'''
生成器执行!
1
2
'''

【例子】用生成器实现斐波那契数列。

def libs(n):
    a = 0
    b = 1
    while True:
        a, b = b, a + b
        if a > n:
            return
        yield a


for each in libs(100):
    print(each, end=' ')

# 1 1 2 3 5 8 13 21 34 55 89

十一.习题

1、上面提到了许多魔法方法,如__new__,__init__, __str__,__rstr__,__getitem__,__setitem__等等,请总结它们各自的使用方法。

  • __new__:类实例化时调用的第一个方法,方法的参数将由它传入__init__,并且它可以决定调不调用__init__方法。
  • __init__:函数初始化时执行的方法。
  • __str__:在使用print()时会自动触发__str__
  • __rstr__:生成随机字符串,但是也需要传入参数。
  • __getitem__:一般会在索引的时候进行试用。
  • __setitem__该方法应该按一定的方式存储和key相关的value。

2、利用python做一个简单的定时器类
要求:

  • 定制一个计时器的类。
  • startstop方法代表启动计时和停止计时。
  • 假设计时器对象t1print(t1)和直接调用t1均显示结果。
  • 当计时器未启动或已经停止计时时,调用stop方法会给予温馨的提示。
  • 两个计时器对象可以进行相加:t1+t2
  • 只能使用提供的有限资源完成。
import time

class Timer():
    def start(self):
        global a #定义全局变量a
        global b #定义全局变量a
        a=time.time() #获取当前时间
        print('启动计时器')
        while(1):
            i=input() #输入指令
            if i == 'print(t1)' : #只有这三个指令能够使时钟停止
                b = time.time() - a
                print(round(b,2))
                break
            elif i=='a.stop()':#只有这三个指令能够使时钟停止
                b = time.time() - a
                print(round(b,2))
                break
            elif i=='t1':   #只有这三个指令能够使时钟停止
                b = time.time() - a
                print(round(b,2))
                break
            else:
                print('请输入正确的指令')

t1=Timer()  #将Timer()实例化
t1.start()  #启动定时器1
t1b=b       #记录t1的时间
t2=Timer()  #将Timer()实例化
t2.start()  #启动定时器2
t2b=b       #记录t2的时间
t=t1b+t2b   #t1和t2的时间和
print(round(t,2))

能完成题目的要求,但是代码不是题目想要的那种哈哈哈哈,纯粹偷换概念写的。
在这里插入图片描述

十二.总结

这是带有魔法的一章,据说非常强大,学得有点云里雾里,知识点越来越难了。加油打工人!

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值