外观
类的方法(二)
前置知识:类未实例化时使用属性和方法
我们先来补充一个前置知识(YuzhenQin 一直想让我在前头就说这个知识,我怕大家混淆类和对象的概念、或产生类不需要实例化即可使用的误解,坚持删掉了)。请看这段代码:
class Abc:
a = 1
b = 2
c = 3
def say_hello(self):
print('Hello!')
print(Abc.a, Abc.b, Abc.c) # 1 2 3
a = Abc()
print(a.a, a.b, a.c) # 1 2 3
Abc.say_hello() # 报错:TypeError: Abc.say_hello() missing 1 required positional argument: 'self'
a.say_hello() # Hello!
Abc.say_hello(a) # Hello!
可以看到,我们不初始化类时,也可以使用类名.属性名
的格式访问类中的变量。
而对于方法,如果我们直接使用类名.方法名()
的方式访问会报错。原因很简单——此时self
关键字没有接收到合法的值。解决方法也十分简单粗暴,我们将一个实例化后的Abc
类的对象a
传入,使用类名.方法名(已实例化的对象)
的格式访问即可。
魔术方法
魔术方法通常用于对一个自定义的对象做 Python 中基础的类型转换、数值和逻辑运算。
由于同类魔术方法之间高度相似,本节文档中,**每类魔术方法只展示一种。**想要了解所有的方法可以查看Python常用魔术方法
str
顾名思义,将类转换为字符串时使用的方法。
运行代码
class User:
name = ''
def __str__(self):
return '名为 ' + self.name + ' 的用户'
a = User()
a.name = '小明'
print(str(a)) # 输出:名为 小明 的用户
print(a) # 事实上 print 本身就输出的是传递的参数转为 str 后的结果,输出:名为 小明 的用户
print(super(User, a).__str__()) # 即不自定义时默认的 __str__ 处理,输出:<__main__.User object at 0x00...>
某些时候将对象转换为字符串调用的不是 __str__
,而是 __repr__
。我们通常用这样的方式来解决此问题:
class ClassName:
def __str__(self):
...
__repr__ = __str__
lt
当使用小于号时使用:x < y
语句会调用 x.__lt__(y)
,并将其返回值作为答案。
运行代码
class User:
name = ''
def __lt__(self, other):
return len(self.name) < len(other.name)
a, b = User(), User()
a.name, b.name = 'abc', 'def'
print(a < b) # False
b.name += 'ghi'
print(a < b) # True
同理可处理小于等于__le__
、大于 __gt__
、大于等于 __ge__
、等于 __eq__
、不等于 __ne__
。
add
当使用加法时使用:x + y
语句会调用 x.__add__(y)
,并将其返回值作为答案。
运行代码
class User:
name = ''
def __add__(self, other):
return self.name + other.name
a, b = User(), User()
a.name, b.name = 'abc', 'def'
print(a + b) # abcdef
同样可以被重新定义的还有减法、乘法、除法、次方、位运算等运算操作。算术运算相关魔术方法
运算操作的左与右
部分运算操作定义存在左右之分。看下面的例子:
class Student:
age = 0
def __add__(self, num):
return self.age + num
a = Student()
print(a + 1) # 输出:1
print(2 + a) # 报错:TypeError: unsupported operand type(s) for +: 'int' and 'Student'
这是由于在第二个加法中,2
在加号左边,所以我们调用了 int
类型定义的加法,而 int
类型并没有兼容我们自定义的类。
此时,我们就要定义 __radd__
方法:
运行代码
class Student:
age = 0
def __radd__(self, num):
return self.age + num
a = Student()
print(2 + a) # 输出:2
大多数运算和比较符都有左右区分,具体实现可以自行搜索。
call
将对象当作一个函数调用。
运行代码
class User:
def __call__(self):
print("I'm a user, why do you want to call me?")
a = User()
a() # I'm a user, why do you want to call me?
staticmethod 和 classmethod
staticmethod
和 classmethod
是 Python 内建的装饰器。
菜鸟教程对装饰器的解释如下:装饰器本质上是一个 Python 函数或类,它可以让其它函数或类在不需要做任何代码修改的前提下增加额外功能。
现在您无需了解什么是装饰器,我们会在以后的教程中提到(吗?如果没有的话请自行 Bing 搜索吧……)
staticmethod
运行代码
def check_password(username, password):
if password == 'password':
return True
return False
class User:
def __init__(self, username):
self.username = username
@staticmethod
def login(username, password):
if check_password(username, password):
return User(username)
return
def __str__(self):
return 'User: ' + self.username
print(User.login('admin', 'password')) # User: admin
print(User.login('admin', 'wrong_password')) # None
staticmethod
被绑定在了一个类中,它的特点是完全可以作为一个函数独立出来,放到类中只是为了代码组织的逻辑,并没有任何实际意义。
classmethod
classmethod
与方法类似,第一个参数必须是 cls
。cls
代表的是类,就像是 self
代表对象一个道理。
运行代码
class Student:
@classmethod
def new_student(cls):
return cls() # 此处的 class 是类 Student 本身,因此 cls() 等同于 Student()
def test(self):
print('ok')
a = Student.new_student() # 在此例中等同于 Student()
a.test() # 输出:ok
实际上,由于将第五行替换为 return Student()
也是合法的,并且具有与当前写法具有相同表现,因此 classmethod
通常只在存在复杂继承关系的情况下被使用。
在已实例化的对象中使用
在上例末尾加上一句 b = a.new_student()
,程序不会报错。因为 new_student()
是类方法,类方法可以被类对象调用。当然,由于此处实例化后的 a
没什么意义,所以这句代码的运行等价于 b = Student.new_student()
。
property
property
可以将一个函数的返回值映射为属性。每次访问该属性,property
会运行被其修饰的函数并将返回值作为属性值。
property
修饰的函数能且只能接受一个参数 self
。通过 property
映射的属性是只读的。
运行代码
current_year = 2022
class People:
def __init__(self, birth_year):
self.birth_year = birth_year
@property
def age(self):
return current_year - self.birth_year
a = People(2000)
print(a.age) # 22
current_year = 2035
print(a.age) # 35
property
通常用于数据缓存。例如:我要通过 a.followers
的格式访问 User
类的某个对象 a
的粉丝信息,我们可以设置 property
检测 a
的粉丝这一数据是否在本地有缓存,若有直接返回,若没有则联网获取。
版权所有
版权归属:异想之旅