山地人

Python 类 Class

山地人
山地人
2021-07-19

上一篇将OOP的时候把面向对象所涉及的知识点都过了一遍,这一篇重点关注类Class。 通过这篇教程的学习,你会掌握:

  1. 在Python中如何定义类
  2. 一个类可以包含哪些成分
  3. 如何创建对象
  4. 如何进行运算符重写
  5. 实例和类的关系
  6. 实例和类中self的关系。

在Python中定义类

在Python中定义一个类使用关键词class加上要定义的类名称就可以定义一个类。

class 类名:
pass

定义属性和方法

然而,这样的一个空类并没有什么功能。我们还需要为这个类定义属性和方法。

  • 属性可以将类中的属性理解成放入这个类的一些数据。
  • 方法可以是公开方法也可以是私有方法,用来帮助类对数据进行处理并进行相应的输出。
class Student {
# 学生类的属性
name = ""
age = 0
# 实例初始化方法
def __init__(self, name, age):
self.name = name
self.age = age
def study(self):
print(r"{self.name}正在学习")
def eat(self):
print(r"{self.name}正在吃饭")
}

以之前讲过的学生类为例子,其中的name和age就是属性。用def定义的都是类中的方法。类中的方法和类外部的普通想法的区别是参数部分多了一个self。这个self会在后面单独来讲。

类中的构造函数

在Python中,构造函数是用来做实例初始化使用的。Python中构造函数有一个固定的名称__init__

def __init__(self, name, age):
self.name = name
self.age = age

这个构造函数出去第一个self参数外,还有两个参数nameage。用于将初始化时外部传入的数据设置到内部的nameage属性上。这样就可以让每个构建出来的实例拥有不同的name和age。

在Python中创建对象

定义好类后,我们可以使用这个类来创建对象。 在Python中创建对象,类似函数调用,通过类名,后面放一对括号,在括号中传入要初始化的数据。最后这些内容会通过类中的__init__函数完成对这个类的实例化。

s1 = Student("张三",16)
s2 = Student("李四", 20)

私有方法

上面定义的studyeat这些方法都是公开的方法。所谓公开就是在类外部是可以访问这些方法的。Python也是可以定义私有方法的,只不过和其他一些语言不同。比如在Java中对于这种类中的私有方法有明确的关键词publicprivate来指明方法到底是公开还是私有。而Python中是通过在方法名称前加__两个下划线来标识方法是否是私有。

class A:
def __private_method(self):
pass
def public_method(self):
pass
  • _private_method就是一个私有方法,在外部不能被调用。

运行下面的程序,并按照内部的提示进行操作。

类中的特殊方法

Python的类中有一些特殊的方法,比如我们定义构造函数时使用的__init__

方法解释
init构造函数
del析构函数
repr打印
str获取对象的字符串表示形式
setItem按照索引赋值函数
getItem按照索引取值函数
len长度运算
cmp比较运算
call函数调用
add加法运算
sub减法运算
mul乘法运算
truediv求除
mod求余数
pow求乘方
getattr访问属性

利用这些特殊的方法,可以让你的自定义类拥有更好的使用体验。

运算符重写

利用上面提供的这些特殊方法,我们在自己定义的类中,对这些特殊方法进行重写。就可以让自定义类支持更多更为方便的操作。

假设定义了一个Product类,它有三个属性:name产品名称,price产品单价和count产品数量。

class Product:
def __init__(name, price, count):
self.name = name
self.price = price
self.count = count
p = Product("包子", 1.5, 2)
print(p)

运行下面的程序,观察print的输出。

<__main__.Product object at 0x7f649c293fa0>

我们用print输出的信息并不是我们所关心的内容,为了获得更好的输出效果,我们对__str__进行重写。

class Product:
...
def __str__(self):
return f"{self.name} {self.price}x{self.count}"

再次运行下面的程序,观察print的输出。

这次输出的信息就比较符合我们的需求了。 ``` 包子 1.5x2 ```

现在我们想对这个Product进行升级,让它支持比如数量的操作。比如:

p = Product("包子", 1.5, 2)
# 让包子的数量增加5个
p = p + 5

为了达到这样的效果,我们需要对+号运算符进行重写。

class Product:
...
def __add__(self, n):
self.count += n
return self

测试下重写后的操作

现在如果要支持这样的操作呢,对两个名称和价格相同但数量不同的商品进行累加操作

p = Product("包子", 1.5, 2)
# 定义另一个同名同价格的商品
p2 = Product("包子", 1.5, 5)
p3 = p + p2
print(p)

我们需要升级之前的加号运算符

class Product:
...
def __add__(self, n):
if(isinstance(n,int)):
self.count += n
elif(isinstance(n,Product)):
if(n.name == self.name and n.price == self.price):
self.count += n.count
else:
print("名称或价格不同,不能相加")
return self

我们在__add__中对传入的n进行检测,如果n是一个普通的int就直接对数量累加,如果n检测是Product则先对两个产品的名称和价格进行比较,确定是同一个商品后再处理数量累加操作。否则提示无法累加。

测试下重写后的操作,并尝试修改p2的名称或者价格,看是否还能正常运行+操作。

经过三个版本的迭代后,你应该对重写操作符有了更深刻的理解,你可以在这个基础上增加更多的操作符重写。比如增加-减号运算符的支持等。

实例和类的关系

如果把类和实例与活字印刷做个对比。类就好比是那块用来印刷的排版好的模板,而实例就是印出来的一页页的印刷成品。我们对类的定义过程就好比是活字印刷前的制版过程。而创建实例的过程就好比是给印刷板刷上不通过的油墨,然后印刷出不同的印刷成品。通过这个比喻你应该对实例和类有了更清晰的认知。

实例和self

在定义类的时候,我们在类中的成员函数都有一个self参数。而且这个self参数放在了参数列表中的第一个位置。这是Python特意规定的。这个self其实指代的就是当前创建的那个实例。通过这个self,我们就可以访问到当前这个实例的方法或者属性。

class Student:
name = "李四"
def study(self):
print(f"{self.name}在学习。")

比如上面的study方法内部,通过self.name可以获取到实例内部的name属性。

至此,本篇教程也到了该和你说再见的时候了,我们下期再见。

学完本篇互动教程,如果你觉得体验不错,可以把网页链接发送给你的小伙伴,让他/她也来感受一下。当然,你也可以继续看看网站上其他的的互动教程,希望`idev365`能够给你带来收获。

学习教程的过程中碰到了问题,或者对idev365有什么改进意见和想法,欢迎加入idev365微信内测群,和山地人交流你的想法。