面向对象(初级)
类
生活中有很多类,人类、鸟类、动物类等等。这些类都有他们共有的属性和能力。那么我们就可以用类来定义一个人类,其中人类的一个实例化变量称为对象。
1.类的作用
类似于函数将一段代码组合至一起,类可以将变量和函数组合至一起。
2.类的定义
类定义首字母需大写
class Person: # 定义一个人类,类中的内容都要在缩进中。
name = "ice"
def eat(self):
print("I am eating")
这里的self将在后面提起。
3.类的变量
可以直接用类.变量
来获取对类的变量,但是直接使用变量则会报错。即使在这个类中的函数里也不能直接使用变量。
print(Person.name) # ice
print(name) # 报错
4.类的方法
与变量一样
Person.eat(Person) # I am eating
5.类的实例化
类似于list.sort()
其实list就是对象,而sort是list对象的一个方法。对象其实就是类的一个实例化对象,和变量定义一样:
ps = Person() # 实例化一个人
5.类的初始化
就是常见的def __init__(self):
这个就是类的初始化函数。会在类实例化的过程当中自动调用。类似__init__
带有下划线的是python中已经拥有的方法,具有特殊能力,称之为魔术方法,不可改名称。
class Person:
name = "ice"
def eat(self):
print(f"{self.name} is eating")
# def __init__(self):
# print("我正在实例化")
def __init__(self, name):
self.name = name
# ps = Person() # 我正在实例化
ps1 = Person("ice") # 实例化一个ice对象
ps2 = Person("冰鸽") # 实例化一个冰鸽对象
ps1.eat() # ice is eating
ps2.eat() # 冰鸽 is eating
由此我们可以定义两个不同的对象。值得注意的是,当一个类没有定义def __init__(self):
时,该类会自动生成一个空的初始化函数,但一旦定义之后,这个空的初始化函数将不存在。并且一个类只能定义一个初始化函数。
6.类中的self
在我们进行实例化对象时,这个self就代表着该对象,在类函数中必须含有第一个参数,而这个参数一般是self。可以理解为该变量作为参数传入函数中。由于在类的函数中不好用某个实例化对象.变量
,则可以改成self.变量
。
面向对象与面向过程
举个例子:吃面条
- 面向过程:
买面条->洗锅->烧水->煮面条->吃面
- 面向对象:
进食堂->点面->吃面
也就是面向对象将买面条->洗锅->烧水->煮面条
这一系列操作封装至进食堂->点面
中。
析构函数
析构函数定义为def __del__(self):
在删除实例化对象时自动调用。我们使用del进行对象的删除。
def __init__(self, name):
self.name = name
def __del__(self):
print(f"{self.name} 被删除了")
ps1 = Person("ice")
del ps1 # ice 被删除了
类的继承和重用
一个类可以继承其他类,从而获得他们的能力。
1.继承
对于人类,有些人主要吃米饭,而有些人主要吃面条,这样就可以分出两个类,但是这两个类相似点太多,如果重新定义的话会导致代码大量重复。于是就出现了继承。
class Person:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print("吃白米饭")
def sleep(self):
print("睡觉")
class Sichuan(Person): # 四川人的类继承人类
def eat(self):
print("四川人喜欢吃火锅")
ps1 = Person("ice", 19, "man")
# ps2 = Sichuan() # 当类继承且本身没有初始化函数时他也要像父类那样实例化
ps2 = Sichuan("冰鸽", 19, "man")
ps1.sleep() # 睡觉
ps1.eat() # 吃白米饭
ps2.sleep() # 睡觉
由代码可以看出ps2对象继承了父类的sleep函数。
补充一下所有的类都继承object类。
2.重用
可以发现Person类与Sichuan类共同具有eat函数,当对象ps2调用eat函数的时候到底时执行哪个呢,显然是执行子类的函数,这个叫做方法的重用。
ps2.eat() # 四川人喜欢吃火锅
3.子类访问父类
子类可以通过super().
的形式访问父类的方法或变量。
class Sichuan(Person, object): # 四川人的类继承人类
def eat(self):
print("四川人喜欢吃火锅")
super().eat()
这样就可以实现既吃火锅又吃米饭了。
多继承
假设现在有一个四川人和一个广东人结婚生下了一个混血儿,此时混血儿就继承两个类了。
class Sichuan(Person, object): # 四川人的类继承人类
def eat(self):
print("四川人喜欢吃火锅")
super().sleep()
class Guangdong:
def eat(self):
print("广东人喜欢喝茶")
class Hunxue(Sichuan, Guangdong):
def eat(self):
print("混血儿喜欢吃汉堡")
super().eat()
hx = Hunxue("混血", 18, "woman")
hx.eat() # 四川人喜欢吃火锅
可以看到多继承时是具有优先级的,在左边的优先级最高,所以上述eat方法输出结果为四川人喜欢吃火锅。
1.查看继承顺序
利用Hunxue.mro
可以得到混血的继承顺序。
<class '__main__.Hunxue'>
<class '__main__.Sichuan'>
<class '__main__.Guangdong'>
<class '__main__.Person'>
<class 'object'>
由此可以看出Sichuan类优先级最高,与此同时可以发现Sichuan继承Guangdong,但当我们单独对这两个类使用mro时他们之间没有直接关系。
即使是这样,如果我们在以上代码的基础上在Sichuan的eat中加入super().eat()
那么他的输出则应该按照出发点(也就是Hunxue)的继承顺序执行。所以他的输出是广东人喜欢喝茶。
上次练习答案
import datetime
# 利用datetime模块,批量生成月份每天的txt文件
import os
def fun1():
for i in range(1, 31):
dt = datetime.datetime(2022, 1, i)
s = dt.strftime("%Y-%m-%d")
# print(s)
file = open("../file/" + s + ".txt", "w")
# 生成上面的文件之后,再一次在每个文件中写入文件名
def fun2():
for i in range(1, 31):
dt = datetime.datetime(2022, 1, i)
s = dt.strftime("%Y-%m-%d")
filename = s + ".txt"
str = "../file/" + filename
file = open(str, "w")
file.write(filename)
file.close()
# 将上面生成的所有文件名之后加上‘_NEW’
def fun3():
for i in range(1, 31):
dt = datetime.datetime(2022, 1, i)
s = dt.strftime("%Y-%m-%d")
filename = s + ".txt"
filename_new = "../file/" + filename
os.rename(filename_new, filename_new + "_NEW")
# 假设有一个英文文本文件,编写一个程序读取其内容,并将里面的大写字母变成小写字母,小写字母变成大写字母
def fun4():
file = open("../file/a.txt", "a+")
file.seek(0)
str1 = file.readlines()
file.close()
file = open("../file/a.txt", "w")
for i in str1:
j = i.swapcase()
file.write(j)
file.close()
# fun1()
# fun2()
# fun3()
# fun4()
练习
- 定义一个账户类,可以创建账户、存款、取款 、查询余额、以及销户等操作。
- 现在三个人分别去开户,存款 和 销户,请利用上面的类实现出来。
结束语
ps:现在关注我,以后就是老粉啦!!!
下篇预告
之前掌握了类的定义和基本使用,可以通过点操作符去访问属性和方法,但是如果属性不存在会怎么办呢?不希望因为属性不存在而出现报错,如何避免这个问题呢?