python 基础 day15魔法方法

魔法方法的“魔力”体现在它们总能够在适当的时候被自动调用

魔法方法的第一个参数应为cls(类方法) 或者self(实例方法)。

  • cls:代表一个类的名称
  • self:代表一个实例对象的名称## 构造与析构

__init__(self[,…])

  • 构造器,当一个实例被创建的时候调用的初始化方法

__init__(self[,…])用于创建对象时使用,每当创建一个类的实例对象时,Python 解释器都会自动调用它。

__init__() 方法可以包含多个参数,但必须包含一个名为 self 的参数,且必须作为第一个参数。也就是说,类的构造方法最少也要有一个 self 参数。

class Rectangle:
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def getPeri(self):
        return(self.x+self.y)*2
    def getArea(self):
        return self.x*self.y
a=Rectangle(5,4)    
print(a.getArea())    #20
print(a.getPeri())    #18

注意 __init__()的返回值只能是None,不能是其他

class Rectangle:
    def __init__(self,x,y):
        return x
a=Rectangle(3,4)    #TypeError: __init__() should return None, not 'int'

__new__(cls[,…])

__new__()才是在一个对象实例化的时候所调用的第一个方法。跟其他魔法方法不同的是,他的第一个参数是cls,

__new__()通常需要返回一个实例对象,通常是cls这个实例化对象,也可以是别的对象
除了继承一个不可变类型时重写一般使用他的默认方案

class Cap(str):
    def __new__(cls, string):
        string=string.upper()
        return str.__new__(cls,string)
a=Cap('play')
print(a)#PLAY

__del__(self)

析构器,当一个对象将要被系统回收之时调用的方法。

Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;以此类推,

与 __init__() 方法对应的是 __del__() 方法,__init__() 方法用于初始化 Python 对象,而 __del__() 则用于销毁 Python 对象,即在任何 Python 对象将要被系统回收之时,系统都会自动调用该对象的 __del__() 方法。

当程序不再需要一个 Python 对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收

class C:
    def __init__(self):
        print("abc")
    def __del__(self):
        print("嘎嘎嘎")
c1=C()
#abc
c2=c1
c3=c1
del c1
del c3
del c2
#嘎嘎嘎
#所有对象的引用被销毁时调用__del__()函数
    

__str__和__repr__

  • __str__(self):

  • 当你打印一个对象的时候,触发__str__

  • 当你使用%s格式化的时候,触发__str__

  • str强转数据类型的时候,触发__str__

  • __repr__(self):

  • _repr__是__str__的备胎

  • 有__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 __repr__(self):
        """返回一个对象的描述信息"""
        return "Cat:(%s,%d)" % (self.name, self.age)

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

c=Cat("cc",10)


print(c)#Cat:(cc,10)
print(repr(tom))  # Cat:(汤姆,30)


#在class Cat中加上一个__str__(self)函数
def __str__(self):
        """返回一个对象的描述信息"""
        return "名字是:%s , 年龄是:%d" % (self.name, self.age)
print(tom)  # 名字是:汤姆 , 年龄是:30

__str__(self) 的返回结果可读性强
__repr__(self) 的返回结果应更准确

例如以下

import datetime

today = datetime.date.today()
print(str(today))  # 2019-10-11
print(repr(today))  # datetime.date(2019, 10, 11)
print('%s' %today)  # 2019-10-11
print('%r' %today)  # datetime.date(2019, 10, 11)

算术运算

工厂函数:类型是type类型,也就是类对象,、当调用时就是创建一个相应的实例对象。

python2.2后,对类和属性进行了统一,做法就是将int(),float(),str(),list(),tuple()这些内置函数转换为工厂函数


#创建的内置函数
print(type(len))#<class 'builtin_function_or_method'>
#工厂函数
print(type(int))#<class 'type'>
class C:
     pass
print(type(C))#<class 'type'>

python的魔法方法提供了自定义对象的数值处理,通过以下魔法方法的重写,可以定义任何对象间的算术运算

算数操作符

在这里插入图片描述

class New_int(int):
#重新定义加法
    def __add__(self, other):
        return int.__mul__(self,other)
#重新定义减法        
    def __sub__(self, other):
        return int.__floordiv__(self,other)
a=New_int(8)
b=New_int(8)
print(a+b)#64 

如果不想调用python的默认方法,注意无限递归

class New_int(int):
    def __add__(self, other):
        return self-other
    def __sub__(self, other):
        return self+other

a=New_int(8)
b=New_int(8)
print(a+b)#RecursionError: maximum recursion depth exceeded
#交错性质的无限循环,调用加法里面调用减法,减法里面调用加法,陷入无限循环

class New_int(int):
    def __add__(self, other):
        return int(self)-int(other)
    def __sub__(self, other):
        return int(self)+(other)

a=New_int(8)
b=New_int(8)
print(a+b)#0
#加入int返回的就是整形的值

反运算

在这里插入图片描述

对于a+b

这里加数是a,被加数是b,因此是a主动,反运算就是a对象的__add__()方法没有实现或者不支持操作,python就会调用b的__radd__()方法

class Nint(int):
#在b的__radd(self,other)__,self是b对象,other是a对象
    def __radd__(self, other):
        return int.__sub__(other, self) # 注意 self 在后面,
a = Nint(5)
b = Nint(3)
print(a + b)  # 8
#默认有__add__()所以b的__radd__()没有执行
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) 定义当一个属性被删除时的行为。
class C:
    def __getattribute__(self, item):
        print('__getattribute__')
        #使用super()调用object基类的__getattribute__()方法
        return super().__getattribute__(item)

    def __getattr__(self, item):
        print('__getattr__')

    def __setattr__(self, key, value):
        print('__setattr__')
        super().__setattr__(key, value)

    def __delattr__(self, item):
        print('__delattr__')
        super().__delattr__(item)

描述符(property原理)

描述符就是将某种特殊类型的类的实例指派给另一个类


特殊类型的类至少要在里面定义__get(),__set(),__delete()__三个特殊方法的任意一个

魔法方法 含义
get(self, instance, owner) 用于访问属性,它返回属性的值。
set(self, instance, value) 将在属性分配操作中调用,不返回任何内容。
del(self, instance) 控制删除操作,不返回任何内容。
class MyDecriptor:
    def __get__(self, instance, owner):
        print('__get__', self, instance, owner)

    def __set__(self, instance, value):
        print('__set__', self, instance, value)

    def __delete__(self, instance):
        print('__delete__', self, instance)
class Text:
     x=MyDecriptor()
t=Text()
t.x
#访问x属性时候,自动调用__set()__方法,self是描述符类自身的实例;instance是这个描述符的拥有者所在的类的实例,在这里就是Text类的实例;owner是这个描述符的拥有者所在的类的本身
#__get__ <__main__.MyDecriptor object at 0x000001F45B50D688> <__main__.Text object at 0x000001F45B50D8C8> <class '__main__.Text'>
t.x = 'x-man'
#对x属性进行赋值操作时,python会自动调用__set()__方法,前两个参数跟__get()__方法是一样的,最后一个参数value是等号右边的值
#__set__ <__main__.MyDecriptor object at 0x000001C8D3CDD808> <__main__.Text object at 0x000001C8D3CDD908> x-man
#最后一个del操作也是同样的道理

del t.x
#__delete__ <__main__.MyDecriptor object at 0x000001C8D3CDD808> <__main__.Text object at 0x000001C8D3CDD908>


定制序列

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

容器类型的协议

  • 如果说你希望定制的容器是不可变的话,你只需要定义__len__()和__getitem()__方法。
  • 如果你希望定制的容器是可变的话,除了__len__()和__getitem__()方法,你还需要定义__setitem__()和__delitem()__两个方法。
魔法方法 含义
len(self) 定义当被len()调用时的行为(返回容器中元素的个数)。
getitem(self, key) 定义获取容器中元素的行为,相当于self[key]。
setitem(self, key, value) 定义设置容器中指定元素的行为,相当于self[key] = value。
delitem(self, key) 定义删除容器中指定元素的行为,相当于del self[key]。

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

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]

    def __setitem__(self, key, value):
        self.values[key] = value

    def __delitem__(self, key):
        del self.values[key]
        for i in range(0, len(self.values)):
            if i >= key:
                self.count[i] = self.count[i + 1]
        self.count.pop(len(self.values))


c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1])  # 3
print(c2[2])  # 6
c2[2] = 12
print(c1[1] + c2[2])  # 15
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}
del c1[1]
print(c1.count)
# {0: 0, 1: 0, 2: 0, 3: 0}

迭代器

迭代的意思类似于循环,每一次重复的过程被称为一次迭代的过程,而每一次迭代得到的结果会被用来作为下一次迭代的初始值,提供迭代方法的容器称为迭代器,通常接触的迭代器序列(列表,元组,字符串)还有字典也是迭代器,都支持迭代操作
简单的例子

在这里插入代码片
  • 迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
  • 迭代器是一个可以记住遍历的位置的对象。
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
  • 迭代器只能往前不会后退。字符串,列表或元组对象都可用于创建迭代器
#for 语句迭代
a=range(1,10)
for i in a:
    print(i,end=' ')#1 2 3 4 5 6 7 8 9
print(a)#range(1, 10)

迭代器有两个基本的内置函数:iter() 和 next()。

  • iter(object) 函数用来生成迭代器。
  • next(iterator[, default]) 返回迭代器的下一个项目。
  • iterator – 可迭代对象
  • default – 可选,用于设置在没有下一个元素时返回该默认值,如果不设置,又没有下一个元素则会触发 StopIteration异常。
str="laohu"
hu=iter(str)
print(next(hu))#l
print(next(hu))#a
print(next(hu))#o
print(next(hu))#h
print(next(hu))#u
print(next(hu))#StopIteration

#使用for循环
string="laohu"
str=iter(string)
while True:
    try:
        s=next(str)
    except StopIteration:
        break
    print(s,end=" ")#l a o h u 


迭代器基于以下两个方法实现:

  • next(self):返回容器的下一个元素。
  • iter(self):定义当迭代容器中的元素的行为,返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next()__方法并通过 StopIteration 异常标识迭代的完成。
  • StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
class countDown:
  def __init__(self,step):
    self.step = step
  def __next__(self):
    if self.step <= 0:
      raise StopIteration
    self.step -= 1
    return self.step
  def __iter__(self):
    return self
for element in countDown(10):
  print(element,end=" ")#9 8 7 6 5 4 3 2 1 0 

## 生成器 生成器也会提供 __next__() 方法,这意味着程序同样可调用内置的 next() 函数来获取生成器的下一个值,也可使用 for 循环来遍历生成器。

生成器与迭代器的区别在于,迭代器通常是先定义一个迭代器类,然后通过创建实例来创建迭代器;而生成器则是先定义一个包含 yield 语句的函数,然后通过调用该函数来创建生成器

  • 创建生成器
    创建生成器的两个步骤
  • 定义一个包含 yield 语句的函数。
  • 调用第 1 步创建的函数得到生成器。
    在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行
    调用一个生成器函数,返回的是一个迭代器对象。
def test(val, step):
    print("--------函数开始执行------")
    cur = 0
    # 遍历0~val
    for i in range(val):
        # cur添加i*step
        cur += i * step
        yield cur

上面函数与前面介绍的普通函数的最大区别在于 yield cur 这行,如果将这行代码改为 print(cur),那么这个函数就显得比较普通了,该函数只是简单地遍历区间,并将循环计数器乘以 step 后添加到 cur 变量上,该数列中两个值之间的差值会逐步递增。

yield cur 语句的作用有两点:

  • 每次返回一个值,有点类似于 return 语句。
  • 冻结执行,程序每次执行到 yield 语句时就会被暂停。

执行以下语句:

# 执行函数,返回生成器
t = test(10, 2)
print('=================')
# 获取生成器的第一个值
print(next(t)) # 0,生成器“冻结”在yield处
print(next(t)) # 2,生成器再次“冻结”在yield处

输出结果

=================
--------函数开始执行------
0
2

生成器是 Python 的一个特色功能,在其他语言中往往没有对应的机制,使用生成器至少有以下几个优势:
  • 当使用生成器来生成多个数据时,程序是按需获取数据的,它不会一开始就把所有数据都生成出来,而是每次调用 next()
    获取下一个数据时,生成器才会执行一次,因此可以减少代码的执行次数。比如前面介绍的示例,程序不会一开始就把生成器函数中的循环都执行完成,而是每次调用
    next() 时才执行一次循环体。
  • 当函数需要返回多个数据时,如果不使用生成器,程序就需要使用列表或元组来收集函数返回的多个值,当函数要返回的数据量较大时,这些列表、元组会带来一定的内存开销。如果使用生成器就不存在这个问题,生成器可以按需、逐个返回数据
  • 使用生成器的代码更加简洁。

推导式

列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表。


它的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是 0 个或多个 for 或者 if 语句。那个表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以 if 和 for 语句为上下文的表达式运行完成之后产生。


列表推导式的执行顺序:各语句之间是嵌套关系,左边第二个语句是最外层,依次往右进一层,左边第一条语句是最后一层。

[x*y for x in range(1,5) if x > 2 for y in range(1,4) if y < 3]

执行顺序

for x in range(1,5)
    if x > 2
        for y in range(1,4)
            if y < 3
                x*y

列表推导式

a = [i for i in range(100) if (i % 2) != 0 and (i % 3) == 0]
print(a)
# [3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 93, 99]

字典推导式
b = {i: i % 2 == 0 for i in range(10) if i % 3 == 0}
print(b)
# {0: True, 3: False, 6: True, 9: False}

集合推导式

c = {i for i in [1, 2, 3, 4, 5, 5, 6, 4, 3, 2, 1]}
print(c)
# {1, 2, 3, 4, 5, 6}

其它

d = 'i for i in "I Love Lsgogroup"'
print(d)
# i for i in "I Love Lsgogroup"

e = (i for i in range(10))
print(e)
# <generator object <genexpr> at 0x0000007A0B8D01B0>
#generator生成器,表明,用普通小括号括起来的就是生成器推导式

print(next(e))  # 0
print(next(e))  # 1

for each in e:
    print(each, end=' ')

# 2 3 4 5 6 7 8 9

#生成器推导式如果作为函数的参数,可以直接写推导式不用加小括号
s = sum([i for i in range(101)])
print(s)  # 5050
发布了47 篇原创文章 · 获赞 5 · 访问量 1929

猜你喜欢

转载自blog.csdn.net/Pang_ling/article/details/102879931
今日推荐