阿里云天池:Task 3Python基础进阶:从函数到高级魔法方法

本学习笔记为阿里云天池龙珠计划Python训练营的学习内容,学习链接为:https://tianchi.aliyun.com/specials/promotion/aicamppython?spm=5176.22758685.J_6770933040.1.6f103da1tESyzu

目录

一、学习知识点概要

二、学习内容

I.函数

1.定义自己的函数

2.函数文档

3.函数参数

4.变量作用域

II.Lambda-表达式

1.定义

2.匿名函数的运用

III.类与对象

1.定义自己的类

2.self

 3.类的构造函数

4.公有和私有

5.类的继承与多态

6.组合

7.类对象和实例对象

8.绑定

9.相关的内置函数

IV.魔法方法

1.基本的魔法方法

2.算术运算符

3.反算术运算符

4.增量赋值运算符

5.一元运算符

6.属性访问

7.描述符

8.定制序列

9.迭代器

三、学习问题与解答

四、学习思考与总结

一、学习知识点概要
本次学习的主要知识有:

函数
Lambda-表达式
类与对象
魔法方法
二、学习内容
I.函数
我在之前的文章中把函数称为方法,其实就是封装在一起的能够完成一些操作的代码块。和其他语言相同,Python不知有许许多多内置的函数,也能够定义自己的函数。和其他语言有些不同的是,Python中的函数也是对象,其他函数的返回值也可以是一个函数,进而去构造一些高阶的函数。

1.定义自己的函数
在Python中定义自己的函数的语法结构为:

def 函数名 (参数):
    "函数文档字符串"
    函数体
    return [返回值]
定义函数时以def关键词为开头,后面为函数名和用小括号括起来的参数,参数可以不止一个
函数执行的是冒号之后的代码
return [返回值]结束函数,中括号[]里面可以填写返回值返回给函数,返回值也可以是一个表达式。如果不带表达式则默认返回None
例:

def mean(x):
    "求序列的均值"
    return [sum(x)/len(x)]          #此处返回的是一个列表
 
a=list(range(4,15))
print(a)                            #[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
print('a的均值为:',mean(a))         #a的均值为: [9.0]
2.函数文档
函数文档是在函数定义中,冒号下一行的字符串,用于解释函数的用法。在命令窗口中准备使用函数时编译器会显示该函数的函数文档。可以用魔法方法function.__doc__返回函数的函数文档,也可以用help(functionname)的方式查看到函数名为functionname的函数的函数文档。

例:

def mean(x):
    "求序列的均值"
    return [sum(x)/len(x)]
 
print(mean.__doc__)
                    #'求序列的均值'
help(mean)
                    #Help on function mean in module __main__:
 
                    #mean(x)
                    #    求序列的均值
3.函数参数
Python 的函数具有非常灵活多样的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。从简到繁的参数形态如下:

位置参数 (positional argument)
默认参数 (default argument)
可变参数 (variable argument)
关键字参数 (keyword argument)
命名关键字参数 (name keyword argument)
参数组合
(1)位置参数

定义函数时直接放在函数形参表中,没有任何操作符的参数。这种参数在调用函数时,位置要固定,除非调用时写出参数名进行赋值。

(2)默认参数

定义函数时在形参表中完成赋值的参数,这类参数称为默认参数。若调用函数时传入了参数,则使用传入的参数值;如果没有传入参数值,则使用默认的参数值。

注:定义函数时默认参数一定要放在位置参数的后面

def printinfo(name, age=8):         #name是位置参数,age是默认参数
    print('Name:{0},Age:{1}'.format(name, age))
 
printinfo('江总',20)            #Name:江总,Age:20
printinfo('小王')               #Name:小王,Age:8
(3)可变参数

可变参数意为可传入的参数的个数是可变的,为任意个

在形参表中写入 *vararg 表示vararg是可变参数
传入到vararg里的参数会被打包成元组
定义时要将可变参数放到位置参数的后面,否则在调用时会因无法给位置参数传值而报错
(4)关键字参数

关键字参数也可以传入任意个

在形参表中写入 **keyarg 表示keyarg是关键字参数
传入到keyarg里的参数会被打包成字典
调用函数给关键字参数传值时以类似 function(…… ,key1=value1,key2=value2,……)赋值的方式将传入关键字参数的键值对打包
(5)命名关键字参数

由于传入关键字参数时,用户传入的键值对内容不受限制,如果要限制传入的关键字的名字可以用命名关键字参数

在形参表中输入 *, 分隔符,会将分隔符后面的参数都视为命名关键字参数
调用函数时,必须要写出命名关键字参数的参数名,否则会报错
命名关键字参数可以有默认值
例:例程来源:https://blog.51cto.com/u_11317783/1953276

def person(name, age, **kw):
    if 'city' in kw:
                     #关键字参数kw中是否有city参数
        pass
    if 'job' in kw:
                     #关键字参数kw中是否有job参数
        pass
    print('name:', name, 'age:', age, 'other:', kw)
 
                     #调用者仍可以传入不受限制的关键字参数
person('Jack', 24, city='Beijing', addr='朝阳', zipcode=123456)
# 如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数
 
def person(name, age, *, city, job):
    print(name, age, city, job)
# 和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数
# 调用方式如下
person('Jack', 24, city='Beijing', job='Engineer')

(6)参数组合

定义函数时可以使用以上5种参数的任意组合,只要符合相关规定即可。但建议不要用太多的参数,否则可能会导致函数代码的可读性降低。

4.变量作用域
与其他热门语言相同,Python中的变量可以分为局部变量和全局变量

定义在函数内部的变量拥有局部作用域,该变量称为局部变量
定义在函数外部的变量拥有全局作用域,该变量称为全局变量
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问
若内部作用域想修改外部作用域的变量时,需要用global或nonlocal关键字,例:
num = 1
 
def fun1():
    global num  # 需要使用 global 关键字声明
    print(num)  # 1
    num = 123
    print(num)  # 123
 
fun1()
print(num)  # 123
函数内部可以调用其他函数,称函数的嵌套
(1)闭包

是函数式编程的一个重要的语法结构,是一种特殊的内嵌函数。
如果在一个内部函数里对外层非全局作用域的变量进行引用,那么内部函数就被认为是闭包。
通过闭包可以访问外层非全局作用域的变量,这个作用域称为 闭包作用域
闭包的返回值通常是函数
例:

def make_counter(init):
    counter = [init]
 
    def inc(): counter[0] += 1
 
    def dec(): counter[0] -= 1
 
    def get(): return counter[0]
 
    def reset(): counter[0] = init
 
    return inc, dec, get, reset
 
 
inc, dec, get, reset = make_counter(0)  #内部的inc,dec,get,reset都是闭包的,可以访问counter
inc()                                #外面的inc,dec,get,reset由于获取的是函数make_counter
inc()                                #返回的内部的四个inc,dec,get,reset,
inc()                                #外部的inc,dec,get,reset函数依然可以对局部变量counter
print(get())  # 3                    #进行操作
dec()
print(get())  # 2
reset()
print(get())  # 0

global关键字声明的是全局变量,如果要在函数里的函数的局部变量的作用域,可用nonlocal关键字。

定义函数时,函数体里面还定义了函数,相当于有两层函数,内层的函数是闭包的,可以访问外层函数的变量,但如果内层函数出现了和外层函数同名的变量(假如称为x)时,内层函数内的代码会用内层函数的同名变量,这表明在两个函数的作用域里有两个不同的x。如果想在内部函数修改外层的变量,则需要在内部函数的变量声明前加上nonlocal关键字,表明内外函数中的这个同名变量是同一个。例:

def outer():
    num = 10
 
    def inner():
        nonlocal num    # nonlocal关键字声明,表明inner内部的num和outer的num是同一个
        num = 100
        print(num)
 
    inner()
    print(num)
 
 
outer()
 
# 100
# 100

(2)递归

函数在内部调用自己,称这个函数为递归函数。递归是一种重要的操作。例:

# 利用循环
n = 5
for k in range(1, 5):
    n = n * k
print(n)  # 120
 
# 利用递归
def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)
 
 
print(factorial(5)) # 120
 加入包sys,用sys.setrecursionlimit(n)可以设置递归的最大次数为n,默认是1000次

II.Lambda-表达式
1.定义
lambda关键词用于定义匿名的函数,匿名函数没有函数名,语法结构为:

lambda argument_list: expression
argument_list为形参表,lambda定义的匿名函数和def定义的函数所用参数相同,但是不用小括号括起来
expression为表达式,相当于函数的返回值
expression中没有return语句,因为expression本身就是返回值
匿名函数有自己的命名空间,匿名函数内部不能访问形参表之外和全局命名空间里的参数
例:

def sqr(x):
    return x ** 2
 
 
print(sqr)
# <function sqr at 0x000000BABD3A4400>
 
y = [sqr(x) for x in range(10)]               #运用def定义的普通函数
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 
lbd_sqr = lambda x: x ** 2
print(lbd_sqr)
# <function <lambda> at 0x000000BABB6AC1E0>
 
y = [lbd_sqr(x) for x in range(10)]           #运用lambda定义的匿名函数
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 
 
sumary = lambda arg1, arg2: arg1 + arg2
print(sumary(10, 20))  # 30
 
func = lambda *args: sum(args)
print(func(1, 2, 3, 4, 5))  # 15

2.匿名函数的运用
匿名函数常常应用于高阶函数中,有两种运用方式:

参数是函数
返回值是函数
例如Python内置的filter(function,iterable)函数和map(function,*iterable)函数中,都有函数作为参数,如果用先用def定义一个常常会有些麻烦,但如果用lambda定义的匿名函数就会简洁不少。

odd = lambda x: x % 2 == 1                          #filter()过滤序列,过滤掉不符合条件的templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #元素,返回一个迭代器对象,如果要转换为列
print(list(templist))                               #表,可以使用list() 来转换
# [1, 3, 5, 7, 9]
 
 
m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5])         #map()根据提供的函数对指定序列做映射
print(list(m1))  
# [1, 4, 9, 16, 25]
 
m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
print(list(m2))  
# [3, 7, 11, 15, 19]
III.类与对象
与C++和Java类似,Python也可以定义自己的类。类是对象的模板,对象是类的实例,类中包含属性和方法(函数)。

1.定义自己的类
我们可以用关键字class定义类,关键字后面跟类名和冒号,以及类的内容,Python中的类名以大写字母开头。例:

class Turtle:  
    # 属性
    color = 'green'
    weight = 10
    legs = 4
    shell = True
    mouth = '大嘴'
 
    # 方法
    def climb(self):
        print('我正在很努力的向前爬...')
 
    def run(self):
        print('我正在飞快的向前跑...')
 
    def bite(self):
        print('咬死你咬死你!!')
 
    def eat(self):
        print('有得吃,真满足...')
 
    def sleep(self):
        print('困了,睡了,晚安,zzz')
 
tt = Turtle()
print(tt)
# <__main__.Turtle object at 0x0000007C32D67F98>
 
print(type(tt))
# <class '__main__.Turtle'>
 
print(tt.__class__)
# <class '__main__.Turtle'>
 
print(tt.__class__.__name__)
# Turtle
 
tt.climb()
# 我正在很努力的向前爬...
 
tt.run()
# 我正在飞快的向前跑...
 
tt.bite()
# 咬死你咬死你!!
 
# Python类也是对象。它们是type的实例
print(type(Turtle))
# <class 'type'>

2.self
Python 中的 self 相当于 C++和Java中的 this 指针。

例:

class Ball:
    def setName(self, name):
        self.name = name
 
    def kick(self):
        print("我叫%s,该死的,谁踢我..." % self.name)
 
 
a = Ball()
a.setName("球A")
b = Ball()
b.setName("球B")
c = Ball()
c.setName("球C")
a.kick()
# 我叫球A,该死的,谁踢我...
b.kick()
# 我叫球B,该死的,谁踢我...

 3.类的构造函数
类的构造函数是一种魔法方法,这个魔法方法为__init__(self[,param1,param2…])

Python中的构造函数可以自己定义,但是函数名必须是__init__(self[,param1,param2…])。在类外给类实例化(产生对象)时会自动调用,调用方法是  类名(参数),参数是__init__()中除self之外的参数。

在定义子类的构造函数时应调用父类的构造函数。如果不调用父类的构造函数,可能会因子类的构造函数覆盖了父类的构造函数而导致从父类继承过来的属性不是自己想要的值。当然,你也可以通过在子类的构造函数里给所有属性赋值来避免这种情况,但是这样在类的属性较多时就会比较繁琐了。

例子可见下文“类的继承”部分

4.公有和私有
在 Python 中定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或变量就会为私有的了。

其余的与C++和Java中的公有私有类似,此处不作赘述。但是要提的是Python中的私有是伪私有,只要用以下形式仍然可以在类外直接访问或调用对象的私有属性或私有方法。

对象._类名__私有属性              #只需在对象的.和私有数据或方法的双下划线__
对象._类名__私有方法              #之间插入下划线 _ 加上类名即可
5.类的继承与多态
定义的类可以用以下语法形式继承父类:

class 子类名(父类名1,父类名2,……,父类名n):
      类的实现
子类可以继承父类的数据和函数。子类必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用。

与Java类似,子类可以重写父类的函数。但是子类在重写构造函数时,由于构造函数名不能变化,子类的构造函数就会覆盖父类的构造函数,这样可能会出现以下状况:

import random
 
class Fish:
    def __init__(self):
        self.x = random.randint(0, 10)
        self.y = random.randint(0, 10)
 
    def move(self):
        self.x -= 1
        print("我的位置", self.x, self.y)
 
 
class GoldFish(Fish):  # 金鱼
    pass
 
 
class Carp(Fish):  # 鲤鱼
    pass
 
 
class Salmon(Fish):  # 三文鱼
    pass
 
 
class Shark(Fish):  # 鲨鱼
    def __init__(self):
        self.hungry = True
 
    def eat(self):
        if self.hungry:
            print("吃货的梦想就是天天有得吃!")
            self.hungry = False
        else:
            print("太撑了,吃不下了!")
            self.hungry = True
 
 
g = GoldFish()
g.move()  # 我的位置 9 4
s = Shark()
s.eat() # 吃货的梦想就是天天有得吃!
s.move()  
# AttributeError: 'Shark' object has no attribute 'x'
'''
Shark类的构造函数没有调用父类Fish的构造函数
导致对象s在实例化时没有x和y两个属性
从而在调用move()函数时访问x和y就会出错
'''

 要在子类的构造函数中调用父类的构造函数,可以有以下两种方法:

class Shark(Fish):  # 鲨鱼
    def __init__(self):
        Fish.__init__(self)             #调用未绑定的父类方法Fish.__init__(self)
        self.hungry = True
 
    def eat(self):
        if self.hungry:
            print("吃货的梦想就是天天有得吃!")
            self.hungry = False
        else:
            print("太撑了,吃不下了!")
            self.hungry = True
 
'''或是用以下方法'''
class Shark(Fish):  # 鲨鱼
    def __init__(self):
        super().__init__()            #用super函数super().__init__()
        self.hungry = True
 
    def eat(self):
        if self.hungry:
            print("吃货的梦想就是天天有得吃!")
            self.hungry = False
        else:
            print("太撑了,吃不下了!")
            self.hungry = True

 其中super函数和Java中的super关键字类似,但是在Python的子类中用super()来表示父类的对象。

在多继承时需要注意圆括号中父类的顺序,若是父类中有相同的函数名,而在子类使用时未指定,Python 从左至右搜索,即函数在子类中未找到时,从左到右查找父类中是否包含该函数。

与Java类似,子类也可以通过重写父类的函数,再通过用子类的构造方法构造父类的对象来实现多态。Java中称用子类构造方法构造的父类对象为上转型对象,调用同名的函数时会调用子类中的,但是子类有而父类中没有的属性和函数会丢失。

多态:不同对象对同一方法响应不同的行动

例:

class Animal:
    def run(self):
        raise AttributeError('子类必须实现这个方法')
 
 
class People(Animal):
    def run(self):
        print('人正在走')
 
 
class Pig(Animal):
    def run(self):
        print('pig is walking')
 
 
class Dog(Animal):
    def run(self):
        print('dog is running')
 
 
def func(animal):
    animal.run()
 
 
func(Pig())                             #Pig()是Pig类的构造函数
# pig is walking

6.组合
即其他类的对象可以成为另一个类的属性,例:

class Turtle:
    def __init__(self, x):
        self.num = x
 
 
class Fish:
    def __init__(self, x):
        self.num = x
 
 
class Pool:
    def __init__(self, x, y):
        self.turtle = Turtle(x)
        self.fish = Fish(y)
 
    def print_num(self):
        print("水池里面有乌龟%s只,小鱼%s条" % (self.turtle.num, self.fish.num))
 
 
p = Pool(2, 3)
p.print_num()
# 水池里面有乌龟2只,小鱼3条

7.类对象和实例对象
在Python中“万物皆对象”,所以我们创建的类也是一个对象,也在内存中开辟了一块空间,这种对象叫类对象,类对象只能有一个。
通过实例化类创建的对象称为实例对象,实例对象可以有多个。
类属性和实例属性:

类属性和实例属性的说法和Java类似,就是静态属性和非静态属性的区别,但是Python中的类属性和实例属性的声明不一样。

在类里面方法外声明的属性是类属性,需要赋值,所有实例对象都可以访问且共享。
实例属性是和具体的实例有关,并且一个实例对象和另外一个实例对象是不共享属性的,说白了实例属性只能在自己的对象里面使用,其他的对象不能直接使用。可以通过类内的函数或类外赋值创建。
注:在类外赋值创建实例属性时,如果属性和函数同名,属性会覆盖函数。
8.绑定
Python 严格要求方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。

Python 对象的数据属性通常存储在名为 .__dict__的字典中,我们可以直接访问 .__dict__ ,或者利用 Python 的内置函数 vars()获取 .__dict__ 。

9.相关的内置函数
issubclass(class,classinfo)  方法用于判断参数 class 是否是类型参数 classinfo 的子类
一个类被认为是其自身的子类
classinfo 可以是类对象的元组,只要class是其中任何一个候选类的子类,则返回True
 

isinstance(object,classinfo)  方法用于判断一个对象是否是classinfo的对象,类似type()
type()不会认为子类是一种父类类型,不考虑继承关系
isinstance()会认为子类是一种父类类型,考虑继承关系
如果第一个参数不是对象,则永远返回False
如果第二个参数不是类或者由类对象组成的元组,会抛出一个TypeError异常
 

hasattr(object,name)  用于判断对象是否包含对应的属性
getattr(object,name[,default])  用于返回一个对象属性值
setattr(object,name,value)对应 getattr(),用于设置属性值,该属性不一定是存在的
delattr(object,name)  用于删除属性
 

class property([fget[,fset[,fdel[,doc]]]])  于在新式类中返回属性值
fget           获取属性值的函数
fset           设置属性值的函数
fdel           删除属性值函数
doc           属性描述信息
IV.魔法方法
Python的魔法方法是一些特殊的方法,又叫魔术方法,例如上文提到的:__init__()。魔法方法是面向对象的 Python 的一切,有着强大的作用。魔法方法有以下特点:

魔法方法总是被双下划线包围
魔法方法总是能够在适当的时候被自动调用
魔法方法的第一个参数应为 cls (类方法)  或者 self(实例方法)。cls代表一个类名称,self代表一个实例对象的名称。

1.基本的魔法方法
__init__(self[,……]) 构造器,即各个类的构造方法,当一个实例被创建的时候调用的初始化方法。
__new__(cls[,……]) 在一个对象实例化的时候所调用的第一个方法,系统会先调用__new__再调用__init__。
注:__new__()至少要有一个参数cls,代表要实例化的类,此参数在实例化时由 Python 解释器自动提供,后面的参数直接传递给__init__()
注:__new__()对当前类进行了实例化,即产生了一个对象,再将对象返回,传给__init__中的self。但是__new__返回的实例必须是当前类的实例才会继续执行__init__。也就是说,如果通过在类中定义了__new__(),使得__new__返回的不是当前类的对象,那当前类中的__init__是不会被调用的,即使返回的是父类的实例也不行。
例:
class A(object):
    def __init__(self, value):
        print("into A __init__")
        self.value = value
 
    def __new__(cls, *args, **kwargs):
        print("into A __new__")
        print(cls)
        return object.__new__(cls)
 
 
class B(A):
    def __init__(self, value):
        print("into B __init__")
        self.value = value
 
    def __new__(cls, *args, **kwargs):
        print("into B __new__")
        print(cls)
        return super().__new__(A, *args, **kwargs)   #返回的__new__中的cls参数为A类
 
b = B(10)
 
# 结果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>

__new__()方法主要是当你继承一些不可变的 class 时(如:int , str ,tuple), 提供给你一个自定义这些类的实例化过程的途径。
__del__(self) 析构器,相当于C++中的析构函数,当一个对象将要被系统回收之时调用的方法。在类中也可以自己定义__del__()的内容,与C++相似。
__str__(self) 当我们打印(print)一个对象的时候、当你使用%s格式化的时候、str()强转数据类型的时候会调用。
__repr__(self)  __repr__是__str__的备用函数,如果没有实现__str__的时候才会执行__repr__()
注:__repr__(obj)内置函数对应的结果是__repr__的返回值
注:当使用%r格式化的时候会调用__repr__
__str__(self)的返回结果可读性强。也就是说,__str__的意义是得到便于阅读的信息,就像下面的 '2019-10-11' 一样。

__repr__(self)的返回结果应更准确。也就是说,__repr__存在的目的在于调试,便于开发者使用。

例:

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)
2.算术运算符
Python中可以在类中定义运算符对应的魔法方法,进而实现类对象的算术运算的操作。

__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()调用时的行为。
注:未定义时power()与**操作结果相同
__lshift__(self,other)                          定义左移运算符 :<<
__rshift__(self,other)                          定义右移运算符 :>>
__and__(self,other)                            定义按位与 :&
__xor__(self,other)                             定义按位异或 :^
__or__(self,other)                               定义按位或 :|
例:

class MyClass:
 
    def __init__(self, height, weight):
        self.height = height
        self.weight = weight
 
    # 两个对象的身高相加,体重相加。返回一个新的对象
    def __add__(self, others):
        return MyClass(self.height + others.height, self.weight + others.weight)
 
    # 两个对象的身高相减,体重相减。返回一个新的对象
    def __sub__(self, others):
        return MyClass(self.height - others.height, self.weight - others.weight)
 
    # 说一下自己的参数
    def intro(self):
        print("高为", self.height, " 重为", self.weight)
 
 
def main():
    a = MyClass(height=10, weight=5)
    a.intro()
 
    b = MyClass(height=20, weight=10)
    b.intro()
 
    c = b - a
    c.intro()
 
    d = a + b
    d.intro()
 
 
if __name__ == '__main__':
    main()
 
# 高为 10  重为 5
# 高为 20  重为 10
# 高为 10  重为 5
# 高为 30  重为 15

3.反算术运算符
反运算魔方方法,与算术运算符保持一一对应,不同之处就是反运算的魔法方法多了一个“r”。当文件左操作不支持相应的操作时被调用。

以定义加法的魔法方法为例,定义算术运算符+用__add__(self,other),反算术运算符+用__radd__(self,other)。上文所提所有运算符都有对应的反算术运算符。

反算术运算符的用处可以用a+b举例,这里a是加数,b是被加数,当使用__add__(self,other)定义的+运算时,会将a传给self,b传给other,也就是调用对象a的__add__()方法。如果定义的__add__(self,other)不支持当前操作,或者__add__(self,other)没有实现,Python就会调用对象b的__radd__(self,other)。换句话说就是如果a+b实现不了,就计算b+a。

例:

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
'''代码解释:
运算1 + b时,1是int类型的对象,b是Nint类型的对象
一开始先尝试调用1.__add__(),由于不支持此类操作
系统便尝试调用b.__radd__(),将b传入self,1传入other
返回的是int.__sub__(),1 + b的运算结果就变成1 - 3 '''
4.增量赋值运算符
Python中有增量赋值运算符,例如 += ,a += 2 与 a = a + 2等价。定义增量赋值运算符的魔法方法与算术运算符的魔法方法一一对应,不同之处在于名字前面多了一个“ i ”

__iadd__(self,other)                       定义赋值加法:+=
__isub__(self,other)                       定义赋值减法:-=
__imul__(self,other)                       定义赋值乘法:*=
__itruediv__(self,other)                  定义赋值真除法:/=
__ifloordiv__(self,other)                 定义赋值整数除法://=
__imod__(self,other)                      定义赋值取模算法:%=
__ipow__(self,other[,module])       定义赋值幂运算:**=
__ilshift__(self,other)                     定义赋值按位左移位:<<=
__irshift__(self,other)                    定义赋值按位右移位:>>=
__iand__(self,other)                      定义赋值按位与操作:&=
__ixor__(self,other)                       定义赋值按位异或操作:^=
__ior__(self,other)                         定义赋值按位或操作:|=
5.一元运算符
__neg__(self)                               定义正号:+x
__pos__(self)                               定义负号:-x
__abs__(self)                               定义被abs()调用时的行为,即绝对值
__invert__(self)                            定义按位取反:~x
6.属性访问
__getattr__(self,name)                 定义当用户试图获取一个不存在的属性时的行为。
__getattribute__(self,name)   定义当该类的属性被访问时的行为(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__)。
__setattr__(self,name,value)        定义当一个属性被设置时的行为。
__delattr__(self,name)                  定义当一个属性被删除时的行为。
例:

class C:
    def __getattribute__(self, item):
        print('__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)
 
 
c = C()
c.x
# __getattribute__
# __getattr__
 
c.x = 1
# __setattr__
 
del c.x
# __delattr__

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

__get__(self,instance,owner)        用于访问属性,它返回属性的值。
__set__(self,instance,value)         将在属性分配操作中调用,不返回任何内容。
__delete__(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 Test:
    x = MyDecriptor()
 
 
t = Test()
t.x
# __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at 0x000000CEABDC0898> <class '__main__.Test'>
 
t.x = 'x-man'
# __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at 0x00000023696B0940> x-man
 
del t.x
# __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at 0x000000EC9B160B38>

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

容器类型相关的魔法方法有:

__len__(self)                            定义当被len()调用时的行为(返回容器中元素的个数)
__getitem__(self,key)              定义 获取容器中元素 的行为
__setitem__(self,key,value)     定义设置容器中指定元素的行为,相当于self[key] = value
__delitem__(self,key)               定义删除容器中指定元素的行为,相当于del self[key]
而容器类型的协议有:

如果希望定制的容器是不可变的,则只需要在类中定义__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]
 
    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}

9.迭代器
迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
迭代器只能往前不会后退。
字符串,列表或元组对象都可用于创建迭代器
迭代器有两个基本方法:iter() 和 next()

iter(obj)                         用于生成迭代器
next(iterator[,default])   返回迭代器的下一个项目。iterator指可迭代对象,即一个迭代器。default是可选参数,如果没有下一个元素时返回默认值default,如果没设置,又没有下一个元素则会报错
例:

links = {'B': '百度', 'A': '阿里', 'T': '腾讯'}
 
it = iter(links)
while True:
    try:
        each = next(it)
    except StopIteration:
        break
    print(each)
 
# B
# A
# T
 
it = iter(links)
print(next(it))  # B
print(next(it))  # A
print(next(it))  # T
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 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象
  例:

def myGen():
    print('生成器执行!')
    yield 1
    yield 2
    
myG = myGen()
for each in myG:
    print(each)
 
'''
生成器执行!
1
2
'''
 
myG = myGen()
print(next(myG))  
# 生成器执行!
# 1
 
print(next(myG))  # 2
print(next(myG))  # StopIteration

三、学习问题与解答
本次学习所遇到的问题还是对大多数方法的不熟悉。尤其本次学习的主要就是方法,包括Python中最重要的魔法方法,在代码运行时什么时候会调用魔法方法并不熟悉。主要还是需要多上手练习才能知道。

四、学习思考与总结
本次学习主要是学学习Python中的函数,或叫方法。从定义普通的函数,匿名函数和魔法函数都有很大的用处。魔法方法和类的运用可以做出很多简洁又包括多方面功能的操作。主要是要多上手才能有更深的了解。
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值