目录
零、类
面向对象编程是一种十分有效的办法。在面向对象编程中,编写表示世界中的事物和情景的类,并基于这些类来创建对象。
根据类创建对象叫做类的实例化。
类的创建和使用
使用类几乎可以模拟任何东西。
来编写一个表示小狗的简单类 Dog,表示任何小狗。绝大部分宠物狗都有名字和年龄,并且他们都会打滚和蹲下。绝大部分小狗都具备以上两种信息(名字和年龄)和两种行为(打滚和蹲下),Dog 类将包含这些内容。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def sit(self):
print(f"{self.name} is sitting now.")
def roll_over(self):
print(f"{self.name} is rolled over.")
首先,一开始定义了一个名为 Dog 的类,在 python 中,约定首字母大写的名称指的是类。
__init__() 方法:类中的函数称为方法,与之前的函数唯一重要的差别就是调用方法的方式。__init__() 是一个特殊方法,当你根据 Dog 类创建新实例时,python 都会自动运行这个方法。开头和末尾都有下划线是为了避免与普通方法发生名称冲突。_init_() 方法定义包含 self、name、age 三个形参,self 必不可少并且必须排在其他形参之前,这是因为 python 在调用这个方法创建 Dog 实例时,会自动传入实参 self。我们将通过实参向 Dog() 传递名字和年龄,self 会自动传递,因此不需要传递他。每当根据 Dog 类创建实例时,都只需给最后两个形参(name 和 age)提供值。
再往下,self.name 和 self.age,两个变量都有前缀 self。以 self 为前缀的变量可供类中所有方法使用,可以通过任何实例来访问。如此可以通过实例访问的变量称为属性。
Dog 类中还定义了两个方法,sit() 和 roll_over(),这些方法执行时不需要额外的信息,因此他们只有一个形参 self
根据类创建实例
可将类视为有关如何创建实例的说明。Dog 类是一系列说明,让 python 知道如何创建表示特定小狗的实例。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def sit(self):
print(f"{self.name} is sitting now.")
def roll_over(self):
print(f"{self.name} is rolled over.")
my_dog = Dog('tutu',2)
print(f"My dog's name is {my_dog.name}.")
print(f"My dog is {my_dog.age} years old.")
结果:
注意:一个很重要很重要的东西,init 两边各有两个 _
我们使用了之前定义的 Dog 类,主程序处第一行先是创建一条小狗,名字是 tutu,年龄是 2。python遇到这行代码,调用 Dog 类的方法 __init__() 创建一个表示特定小狗的实例,并使用提供的值赋给 name 和 age。然后,python 返回这个实例,并赋给 my_dog
命名约定:通常可以认为首字母大写的名称为类,小写的名称指的是根据类创建的实例
访问属性:访问实例的属性,可使用句点表示法。比如上述例子中使用了 my_dog.name 来访问 my_dog 的属性 name 的值。这样能够很好的表示,比如 my_dog.name 表示 python 先找到实例 my_dog,再查找与该实例相关联的属性 name。
调用方法:创建实例后,就能使用句点表示法来调用 Dog 类中定义的任何方法了。比如让小狗蹲下和打滚
调用方法时,可指定实例的名称(my_dog)和调用的方法,并用句点分隔。遇到代码 my_dog.sit() 时,python 在类 Dog 中查找方法 sit() 并运行其代码。
class Dog:
--snip--
my_dog = Dog('tutu',2)
my_dog.sit()
my_dog.roll_over()
结果:
创建多个实例
实例不是只有一个,我们可以创建多个实例,比如再创建一个 her_dog
class Dog:
--snip--
my_dog = Dog('tutu',2)
my_dog.sit()
her_dog = Dog('lala',3)
her_dog.roll_over()
结果:
壹、使用类和实例
可以使用类来模拟现实生活中的很多情景。
Fruit 类
现在编写一个有关水果的类。
class Fruit:
def __init__(self,breed,quantity):
self.breed = breed
self.quantity = quantity
def describ_fruit(self):
print(f"the {self.breed} have {self.quantity} kg.")
add_fruit = Fruit('apple',20)
add_fruit.describ_fruit()
结果:
给属性指定默认值
我们在创建实例时,有些属性无需通过形参定义,在方法 __init__() 中为其指定默认值即可。
我们添加一个名为 time 的属性,其初始值为 1,表示水果送来 1 天了,并且再添加一个 read_time() 的方法,用于读取水果送来的时间。
class Fruit:
def __init__(self,breed,quantity):
self.breed = breed
self.quantity = quantity
self.time = 1
def describ_fruit(self):
print(f"the {self.breed} have {self.quantity} kg.")
def read_time(self):
print(f"It's been here for {self.time} days.")
add_fruit = Fruit('apple',20)
add_fruit.describ_fruit()
add_fruit.read_time()
结果:
结果显示正确。但是并不是所有的水果都是刚刚送来的,有的可能已经送来好几天了,所以需要修改该属性的值。
修改属性值
直接修改属性值
这是最简单的办法,我们可以直接修改属性的值。
class Fruit:
--snip--
add_fruit = Fruit('apple',20)
add_fruit.describ_fruit()
add_fruit.time = 2
add_fruit.read_time()
结果:
add_fruit.time = 2,这行代码让 python 在实例 add_fruit 中找到属性 time,并将其值设置为 2
通过方法修改属性的值
如果有方法能替你更新属性,就无需直接访问属性,而可将值传递给方法,由它在内部进行更新。
class Fruit:
--snip--
def update_time(self,days):
self.time = days
add_fruit = Fruit('apple',20)
add_fruit.describ_fruit()
add_fruit.update_time(3)
add_fruit.read_time()
结果:
我们在类中添加了新方法 update_time(),这个方法接受一个值,并将其赋给 self.time,然后在后面调用这个方法并为它提供实参
通过方法对属性值进行递增
我们卖水果,随着一天过去,应该给时间增加一天,而不是每天都为他设置新值。为此,我们可以定义一个方法
class Fruit:
--snip--
def addday_time(self):
self.time += 1
add_fruit = Fruit('apple',20)
add_fruit.describ_fruit()
for i in range(9):
add_fruit.addday_time()
add_fruit.read_time()
结果:
贰、继承
编写类时,并不一定非要从无到有,如果要编写的类有另一个现成的特殊版本,我们就可以使用继承 ,一个类继承另一个类,就会自动获得另一个类的所有属性和方法。原有的类称为父类,而新类称为子类,子类继承了父类所有的属性和方法,同时还可以定义自己的属性和方法。
子类的方法 __init__()
在既有类基础上编写新类时,通常要调用父类的方法 __init__()。这将初始化在父类 __init()__() 方法中定义的所有属性,从而让子类包含这些属性。
我们来看一下进口水果的这个例子
class Fruit:
def __init__(self,breed,quantity):
self.breed = breed
self.quantity = quantity
self.time = 1
def describ_fruit(self):
print(f"the {self.breed} have {self.quantity} kg.")
def read_time(self):
print(f"It's been here for {self.time} days.")
def update_time(self,days):
self.time = days
class Imported_Fruit(Fruit):
def __init__(self,bread,quantity):
super().__init__(bread,quantity)
add_fruit = Imported_Fruit('banana',100)
add_fruit.describ_fruit()
结果:
创建子类时,父类必须包含在当前文件中,且位于子类前面。然后定义子类 Imported_Fruit,定义子类时必须在圆括号内指定父类的名称。方法 __init__() 接受创建 Fruit 实例所需的信息。super() 是一个特殊函数,让你能够调用父类的方法。
为子类定义属性和方法
继承之后,我们就可以添加区分子类和父类所需的新属性和新方法了。
下面我们添加一个属性,进口国家。
class Fruit:
--snip--
class Imported_Fruit(Fruit):
def __init__(self,breed,quantity):
super().__init__(breed,quantity)
self.country = 'Vietnam'
def from_country(self):
print(f"the {self.breed} are from {self.country}")
add_fruit = Imported_Fruit('banana',100)
add_fruit.describ_fruit()
add_fruit.from_country()
结果:
我们在子类中新添加了属性 self.country,并设置初始值为 Vietnam,根据 Import_Fruit 类创建的所有实例都包含该属性,但所有 Fruit 实例都不包含它。
之后又添加了一个名为 from_country() 的方法,打印产地信息。
当我们真正在使用时,你可能会发现,随着你代码越写越多,你给类添加的细节也就越来越多,属性和方法清单以及文件都越来越长,此时我们就可以将类的一部分提取出来作为一个独立的类,将一个大类拆分成多个协同工作的小类。
叁、导入类
导入单个类
我们将 Fruit 类单独存放在文件中,并命名为 fruit.py,模块 fruit.py 如下代码所示,里面只包含 Fruit 类代码
class Fruit:
def __init__(self,breed,quantity):
self.breed = breed
self.quantity = quantity
self.time = 1
def describ_fruit(self):
print(f"the {self.breed} have {self.quantity} kg.")
def read_time(self):
print(f"It's been here for {self.time} days.")
def update_time(self,days):
self.time = days
然后我们创建另一个文件,在其中导入 Fruit 类并创建其实例。
from fruit import Fruit
add_fruit = Fruit('apple',20)
add_fruit.describ_fruit()
结果:
import 语句让 python 打开文件 fruit,并导入其中的 Fruit 类,这样就能使用 Fruit 类。
导入类是一种十分有效的编程方式,如此一来,主程序就显得十分简练,并且配合有意义的类名和实例名,能够很清楚的读懂代码的意义。
在一个模块中存储多个类
我们可以根据需要在一个模块中存储任意数量的类。我们将之前所写的 Imported_Fruit 类也加入 fruit 中。
class Fruit:
--snip--
class Imported_Fruit(Fruit):
def __init__(self,breed,quantity):
super().__init__(breed,quantity)
self.country = 'Vietnam'
def from_country(self):
print(f"the {self.breed} are from {self.country}")
然后,我们就可以导入 Imported_Fruit 类,并创建其相关实例
from fruit import Imported_Fruit
add_fruit = Imported_Fruit('banana',100)
add_fruit.describ_fruit()
add_fruit.from_country()
结果:
其他
在一个模块中导入多个类时,用逗号分隔各个类即可,导入相关类后,就可以创建相对应的实例。
导入模块中所有类时,应使用 from 文件名 import *
在模块中可以导入另一个模块
可以使用 as 为类及模块改名
肆、python 标准库
python 标准库是一组模块,我们已经安装的 python 都包含。说的简单点,标准库就是优秀的程序员写好的模块,你可以直接使用。
模块 random
randint() 会随机返回一个位于这两个整数(含)之间的整数
>>> from random import randint
>>> randint(1,6)
6
>>> randint(1,6)
2
choice() 会随即返回一个列表或元组中的一个元素
>>> from random import choice
>>> name = ['lisa','tom','jerry']
>>> choice(name)
'tom'
简单案例—猜数游戏
只是个雏形,比较简陋,可自行扩展
from random import randint
print("猜数游戏,我想一个100以内的数字,来猜猜我想的数字是多少吧")
guess_num = randint(0,99)
sum = 0
while 1:
enter_num = int(input("输入你猜的数字"))
if enter_num < guess_num:
print("猜小了")
sum += 1
elif enter_num > guess_num:
print("猜大了")
sum += 1
elif enter_num == guess_num:
sum += 1
print(f"猜对了,你一共猜了{sum}次")
break