2.1.5 面向对象:类的方法(二)(Python)

前置知识:类未实例化时使用属性和方法

我们先来补充一个前置知识(@金蛋(jindan-uwtta) 一直想让我在前头就说这个知识,我怕大家混淆类和对象的概念、或产生类不需要实例化即可使用的误解,坚持删掉了)。请看这段代码:

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 中基础的类型转换、数值和逻辑运算。

类型转换的魔术方法只接受self一个参数。

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...>

repr

__str__将对象转为供人阅读的格式不同,这个是转换成供 Python 解释器阅读的。

额……说实话我没有想到很巧妙的例子,就先随便放一个吧。你需要知道的是,当你定义了__repr__而没有定义__str__时,将对象转为字符串也会使用__repr__的结果,因此一些人喜欢直接使用__repr__

# 一个并不恰当的例子

class User:
    name = ''

    def __repr__(self):
        return '123'


a = User()
print(a)  # 123

bool

class User:
    name = ''

    def __bool__(self):
        return self.name != ''


a = User()
print(bool(a))  # False
a.name = '小明'
print(bool(a))  # True

len

指定len函数如何计算对象的长度

class User:
    name = ''

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


a = User()
print(len(a))  # 0
a.name = 'abc'
print(len(a))  # 3

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

同理有__sub__ 减法``__mul__ 乘法``__truediv__ 除法以及次方、位运算等运算操作。

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?

更多的魔术方法可以参考这篇文章:Python常用魔术方法 - 知乎

staticmethod 和 classmethod

staticmethodclassmethod是 Python 内建的装饰器。

菜鸟教程对装饰器的解释如下:装饰器本质上是一个 Python 函数或类,它可以让其它函数或类在不需要做任何代码修改的前提下增加额外功能。

现在您无需了解什么是装饰器,我们会在以后的教程中提到(吗?如果没有的话请自行 Bing 搜索吧……)

staticmethod

def check_password(username, password):
    ...


class User:

    def __init__(self, username):
        self.username = username

    @staticmethod
    def login(username, password):
        if check_password(username, password):
            return User(username)
        return


print(User.login('admin', 'right_password'))  # <__main__.User object at 0x00...>
print(User.login('admin', 'wrong_password'))  # None

staticmethod被绑定在了一个类中,它的特点是完全可以作为一个函数独立出来,放到类中只是为了代码组织的逻辑,并没有任何实际意义。

如果不用staticmethod,刚才这个例子也可以这样写:

def check_password(username, password):
    ...


class User:

    def __init__(self, username):
        self.username = username


def login(username, password):
    if check_password(username, password):
        return User(username)
    return


print(login('admin', 'right_password'))  # <__main__.User object at 0x00...>
print(login('admin', 'wrong_password'))  # None

classmethod

classmethod同理,classmethod的第一个参数必须是clscls代表的是类,就像是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通常只在存在复杂继承关系的情况下被使用。

然而,在复杂的继承关系下使用这样子跨类的语句实现十分容易出错,所以classmethod使用频率极低,我们通常使用staticmethod替代之。

在已实例化的对象中使用

在上例末尾加上一句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

a.age = 36  # 报错:AttributeError: can't set attribute 'age'

property通常用于数据缓存。例如:我要通过a.followers的格式访问User类的某个对象a的粉丝信息,我们可以设置函数检测a的粉丝这一数据是否在本地有缓存,若有直接返回,若没有则联网获取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

异想之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值