开发者学堂课程【Python入门 2020年版:魔法方法介绍】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/639/detail/10376
魔法方法介绍
内容介绍:
一、self 的注意事项
二、算数相关的魔法方法
三、转换字符串
一、 self 的注意事项
先新建一个 python 文件,名为02-self 的注意事项
新建一个类:
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def eat(self):
print(self.name +
‘正在吃东西’)
p1 = Preson(
‘张三’,18)
p2 = Person(‘李四’,20)
到现在一共有两个对象,把__init__()方法当作函数,形参是在赋值的时候可以用。如果执行的是 p1 = Preson(‘张三’,18)这段代码,此时 self 指的是 p1创造的内存空间的。
如下图所示:
当 p1指向内存空间时,self 也指向对应的内存空间。p1走完后,__init__调完后,self 不再指向 p1的内存空间,会变为空。
当执行 p2时,会再次调用__init__,它就会指向 p2的内存空间。也就时执行的时候会调用__init__函数,不调用的时候方法就为空,谁调用就指向谁。
但如果加入语句 p1.eat(),此时__init__()中的 self 是都没有指向的,而 eat()中的 self 是指向 p1的内存空间。再函数正在执行的时候,才有指向,等到调完之后,self 谁也不指。
也就是调的时候才有指向,不调就没有指向。所以__init__()和eat()中的 self 是没有可比性的,只有调用的时候才知道self 是谁。
二、 算数相关的魔法方法
新建一个 python 文件,并命为03-运算符相关的魔法方法
书写代码:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
p1 = Preson(
‘zhangsan’,18)
p2 = Person(
‘zhangsan’,18)
print(p1 is p2)
运行结果为:
False,这是两块内存,不过数据内容相同。
加入语句 print(p1 == p2)
整理代码:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __eq__(self,other):
pass
p1 = Preson(
‘zhangsan’,18)
p2 = Person(
‘zhangsan’,18)
print(p1 is p2)
print(p1 ==p2)
运行结果为:
False,原因是
==运算符的本质其实是调用对象的__eq__方法,获取__eq__方法的返回值结果。a == b 其实就是 a.__eq__(b)
print(p1 == p2)就是 p1.__eq__()方法调用到 p2上。现在的问题是在于__eq__(self,other):语句中的 self 指的是谁?
它指的是 p1,因为在之前说过谁调用,self 指的就是谁。所以 p1指向的是 self,p2指向的是 other。现在依照若 p1的内容等于 p2的内容来书写代码:
def __eq__(self,other):
if self.name == other.name and self.age == other.age:
return True
return False
(1)在向代码中加入 print(p1 !=p2)
整理代码内容:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __eq__(self,other):
return self.name == other.name and self.age == other.age:
p1 = Preson(
‘zhangsan’,18)
p2 = Person(
‘zhangsan’,18)
print(p1 is p2)
print(p1 ==p2)
print(p1 !=p2)
运行结果为:
结果是 False
!=本质是调用__ne__方法(not equal)或者__eq__方法取反,虽然没有写这个方法,但只要具有__eq__方法后取反的效果是一样的。
加入代码:
return ‘hello’
整理代码内容:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __eq__(self,other):
return self.name == other.name and self.age == other.age:
def __ne__(self,other):
return
‘hello’
p1 = Preson(
‘zhangsan’,18)
p2 = Person(
‘zhangsan’,18)
print(p1 is p2)
print(p1 ==p2)
print(p1 !=p2)
运行之后:
!=运算就是调用__ne__方法的结果,如果没写这个方法。它就会找到__eq__方法取反来返回这个值。!=运算符会自动调用__ne__这个方法。默认都不写的时候,会比较内存地址
(2)加入 print(p1 >p2)
新加入一个对象 p3 = print(‘lisi’,19)
此时,运行结果如下:
会报错,它不支持写 p1>p2
需要添加
def __gt__(self,other):
return self.age > other.age
当 greater than 使用 > 会自动调用上面的方法
还有__ge__(self,other)方法,它是大于等于时会自动调用。
举一个例子:
num1 = [1,2,3]
num2 = [1,2,3]
print(num1 == num2)
这个用来判断两个之间的内容相等,但是系统自带的,会重写列表。重写的话就会指定规则,在机器上,不管写没写__ge__方法或__gt__方法,都会自动调用,但是不写就调用的话会报错。
如果不写就不能比较。3>2可以的原因是它们的类型是 int,再 class int 里实现了这些方法就可以使用。自定义的类,没有实现方法就使用那么就会报错。
在字典中比较,加入语句{‘name‘:’zhangsan‘} >{’name‘:’jack’}
运行结果为:
字典之间不能用>运算,原因是系统中的dict的类,没有重写机器代码的类,内部写的__gt__方法存在漏洞。
修改{‘name‘:’zhangsan‘} >{’name‘:’jack’}为{‘name‘:’zhangsan‘} >’lisi‘,运行结果依旧报错。
在字典和字典之间重写了这个方法,这可能是有不同类型的比较是不可行的。
修改为{‘age’:18} >12,得运行结果为:
依旧是不可以比较的。机器重写得这个方法应该是满足某些条件是可以比较的
代码具体得写法,是有自己的内部实现。
加入 print(p1 > 20),这样的写法会在运行时报错。原因是原来的 print(p1 >p2)是比较两个对象,原来的 p1.age和p2.age 进行的比较,给的是20的话,这个数字是不具备 age 属性的。所以具体的实现和报错,是由内部实现来决定的。
如果把__gt__方法的返回值改为 return self.age > other,写成 print(p1 > 12)就是可以的,这跟内部的实现有关。
运行结果如下:
就是把 p1的 age 传给 self.age,12直接传给 other,然后两者直接进行比较。自己写代码时,内部的实现究竟是怎样的是自己决定的。不需要管系统的内部是如何实现的,它的实现还是要依赖内部的逻辑是什么,只要知道原理即可。
(3)小于运算
只要写小于号就会调用 def __it__(self,other):less than p1<p2。
def __le__(self,other):这个是小于等于。这几个运算,当需要用到哪一个时,都会调用相应的运算。print(p1 >p2)中是传一个12还是一个 p2?
这个要看内部实现里面怎样的逻辑,如果是方法的返回值要拿到 other.age,这个对象就必须要有 age 属性;如果返回值是一个 other 那么就选择写12;后面比较的值会传给 other。
(4)需要 p1加 p2,单运行的话它是不支持的需要添加方法
return self.age+other.age
print(p1+p2)
整理代码为:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __eq__(self,other):
return self.name == other.name and self.age == other.age:
(5)p1-p2
加入代码:
return self.age
– other
print(p1
–p2)
在方法中写的是年龄相加那么就执行年龄相加,如果是姓名就是姓名相加。
减法中不能 print(p1 – p2)这样写,减法中返回的是对象,一删就会删掉对象,运行代码时会出错,所以 print(p1 – p2)中 p2的所在位置只能写数字
整理代码为:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __eq__(self,other):
return self.name == other.name and self.age == other.age:
最后到底是加一个对象还是数字,关键是在于内部的代码是如何实现的。
在学习中,一切都是有迹可循的,为什么字符串和字符串在这可以加,在那不可以加,原因就是这样。在做方法实现的时候,就会体现什么可以加,什么不可以加。
(6)__mul__乘法
def __mul__(self,other):
return self.name * other
print(p1 * 2)
(7)除法 def __truediv__(),除了前面讲过的之外,还有一些
取模运算:
def __mod__(self,other):
return self.age % other
幂运算:会自动调用
return self.age ** other
整理代码为:
class Person:
def __init__(self,name,age):
自定义所需要进行的运算以及运算的规则,例如允许做加法运算,写上代码后在做规则约束
三、 转换为字符串
print(str(p1))默认会转换成为类型+内存地址
str()将对象转换成为字符串,会自动调用__str__方法
1. 类型转换时会调用str(),str(p1)
2. 直接打印对象也会调用,print(str(p1))
def __str__(self):
return
‘hello’
x = str(p1)
print(x)
整理代码为:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __eq__(self,other):
return self.name == other.name and self.age == other.age:
def __ne__(self,other):
类型转换就是把一个数据转换成字符串,依旧会调用__str__方法并返回它的结果。
还可以转换为数字,比如加入 print(int(p1)),把 int(‘1234’)转换为1234,此时运行会报错。
报错的原因是,当 int(),会调用__int__的结果
加入
def __int__(self):
return 20
这些方法的调用都是魔法方法,除了这些之外还可以加入 print(float(p1)),运行后结果会报错。也是加入一个方法 def __float__(self):
return 100.5
整理代码为:
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def __eq__(self,other):
return self.name == other.name and self.age == other.age:
def __ne__(self,other):
return
‘hello’
def __gt__(self,other):
return self.age > other.age
def __add__(self,other):
p1 = Preson(
‘zhangsan’,18)
p2 = Person(
‘zhangsan’,18)
p3 = print(‘lisi’,19)
运行后的结果为:
Python 语言是非常灵活且强大的,通过本次代码的编写,可以窥探出编程语言是如何做的,它的本质到底是怎么实现的。
掌握的好的话,可以自己写一个编程语言,在学习之后可以知道 python 语言是怎么实现的。
比如写了一个 int(‘hello’)
就是调字符串:
class str:
def __int__(self):
Return
先调用 int,后打印出 hello
如果是 print(int(p1))只调用__int__方法,没有调用到__str__方法,只有 print(p1)才能够调用__str__方法。
这些内容需要了解,它涉及 python 语言的思想。这个还要区分好内置类和内置函数,在这里用内置类进行的调用转换。