class Animal:
def __init__(self, name):
self.name = name
self.appetited = 0
self.starve = 0
def eat(self, volume):
self.starve += volume / self.appetited
self.starve = min([self.starve, 1])
return self.starve自定义类
1 面向对象的程序设计
Object-oriented Programming
1.1 为什么需要类和对象
很多时候我们要描述的东西是比较复杂的。 比如,要描述一个小区,我们可能需要以下信息:
{
'name': '小区名称',
'location': (112.234345, 30.103932),
'developer': '开发商',
'avg_price': 12000,
'buildings': ['#1', '#2', '#3'],
'is_selling': True,
'properties': ['1-101', '1-102', '1-201']
}而且可能还有一些以小区为单位的操作,比如:总户数 count_properties() 。 这些函数是和“小区”这个类型的变量强绑定的,不能应用于其他类型上。 此时使用内置类型就无法满足我们的需要。
1.2 类和对象
Object,一般翻译为“对象”,也被称为“实例”、“实体”。 具有一定关联的变量和函数的集合,用于整体化地描述某个物体、概念或资源, 从而将零散的代码整合起来。
类描述一个对象拥有哪些变量和函数。 必须拥有一个 __init__() 函数来初始化变量。
class Community:
def __init__(self):
self.title = "小区名"comm = Community()
print(comm.title)1.3 四个概念
- 抽象:将某种物体或概念的特点抽象为一组变量。
- 封装:将该种物体或概念的能力包装为一组函数。
- 继承:一个类(基类)进行扩展和更改,得到其他类(子类)。
- 多态:子类种来自基类的函数可以有和基类不同的行为。
Python 中能用到的所有的变量,都是经过抽象和封装的类对象。
1.4 案例:猫、家猫、虎
- 猫
- 花纹、皮毛、尾巴、年龄
- 叫、进食
- 家猫
- 主人、疫苗、宠物证
- 喵喵叫、缠人、蹲坐
- 虎
- 野生或饲养、活动区、标识
- 咆哮、捕猎


1.5 优缺点
- 抽象和封装可以将代码按照实体组合起来,并赋予其实际含义,可以更好地组织和理解代码。
- 继承有助于少写代码,减少出错的可能;多态又保持了一定的灵活性。
但是:
- 抽象和封装要合适,不适当的封装会造成困难,过度封装则会增加维护难度
- 基类太多有的时候会导致代码容易出错,也容易造成冲突
2 编写类
声明类包含哪些变量和函数的过程。
2.1 构造函数
在构造函数 __init__() 中定义类包含哪些变量。
class Property:
def __init__(self):
self.price = 20000
self.size = 100
self.bedroom = 2
self.bathroom = 1
self.decorated = True
self.community = Community()类的所有函数1都必须包含一个 self 参数,而且必须是第一个参数。
构造函数除了定义变量外,还有一个重要作用是传递变量的初始值。
class Appartment:
def __init__(self, price, size, bedroom, bathroom, decorated, community):
self.price = price
self.size = size
self.bedroom = bedroom
self.bathroom = bathroom
self.decorated = decorated
self.community = community
appartment1 = Appartment(20000, 100, 2, 1, True, comm)
print(appartment1.price)2.2 成员变量
- 成员变量是只属于类对象的变量,其他地方无法获取。
- 同一个类的不同对象的同一个变量,也是不同值的。
- 在类对象后面使用
.变量名的方式使用成员变量,也可以修改值。
appartment1.price = 15000
print(appartment1.price)2.3 成员函数(方法)
成员函数的编写方法和普通函数一样,但是可以通过 self 调用成员变量,而普通函数是不可以使用 self 的。
class Appartment:
def __init__(self, price, bedroom, bathroom):
self.price = price
self.bedroom = bedroom
self.bathroom = bathroom
def info(self):
print(f"{self.bedroom}b{self.bathroom}b appartment, {self.price} per week.")
appartment1 = Appartment(400, 1, 1)
appartment1.info()- 在类对象后面使用
.函数名(实参)的方式调用成员函数。 - 调用成员函数的时候,不需要明写
self参数。
成员函数也可以有自己的参数,使用自己的参数时无需使用 self。
class RentalAppartment:
def __init__(self, price, bedroom, bathroom):
self.price_per_week = price
self.bedroom = bedroom
self.bathroom = bathroom
def total_rent(self, weeks):
return self.price_per_week * weeks
appart1 = RentalAppartment(400, 1, 1)
appart1.total_rent(52)成员函数参数也可以有默认值。
2.4 继承和派生
通过扩展一个类(基类)得到另一个类(子类、派生类)的过程。
class 派生类(基类):
// 类定义- 基类可以不止有一个,但最好只有一个。
- 子类中包含基类的所有变量和函数。
- 子类中的函数可以使用
super()表示基类。 - 如果子类和基类有同样的变量或函数,子类中优先使用自己的。
例如
class Cat(Animal):
def __init__(self, name):
super().__init__(name)
self.appetited = 30
def sound(self):
print("Meow!")Cat 类型的变量就可以使用 Animal 的所有成员变量和成员函数。
tcell = Cat("Tcell")
print("Starve level: {0:.2f}%".format(tcell.starve * 100))
print("after eat: {0:.2f}%".format(tcell.eat(20) * 100))Starve level: 0.00%
after eat: 66.67%
tcell.sound()Meow!
3 内置类
Python 的一些内置类提供了很多方法。
3.1 字符串
"Python".lower()'python'
"Python".upper()'PYTHON'
"Python".swapcase()'pYTHON'
"python".capitalize()'Python'
"Python".startswith("Py")True
"Python".endswith("on")True
"Python".find("th")2
"Python,Java,C++".split(",")['Python', 'Java', 'C++']
"Python,Java,C++".replace(",", " | ")'Python | Java | C++'
", ".join(["Python", "Java", "C++"])'Python, Java, C++'
" Python. ".strip()'Python.'
"Result: {0:.2f}".format(1/3)'Result: 0.33'
"Result: {res:.2f}".format(res=1/3)'Result: 0.33'
3.2 列表
a = [1,2,3]
a.append(4)
a[1, 2, 3, 4]
a.extend([4,5,6])
a[1, 2, 3, 4, 4, 5, 6]
print(a.pop())
a6
[1, 2, 3, 4, 4, 5]
a.reverse()
a[5, 4, 4, 3, 2, 1]
a.insert(2, 9)
a[5, 4, 9, 4, 3, 2, 1]
a.remove(4)
a[5, 9, 4, 3, 2, 1]
print(a.pop(2))
a4
[5, 9, 3, 2, 1]
a.clear()
a[]
3.3 字典
d = {
'name': '小区名称',
'developer': '开发商'
}
d.items()dict_items([('name', '小区名称'), ('developer', '开发商')])
d.keys()dict_keys(['name', 'developer'])
d.values()dict_values(['小区名称', '开发商'])
for key, value in d.items():
print(f"{key}={value}")name=小区名称
developer=开发商
d.get('name', '默认名称')'小区名称'
d.get('city', '郑州')'郑州'
d.update({'city': '郑州'})
d{'name': '小区名称', 'developer': '开发商', 'city': '郑州'}
print(d.pop('city'))
d郑州
{'name': '小区名称', 'developer': '开发商'}
{}.fromkeys(['bed', 'bath'], 2){'bed': 2, 'bath': 2}
4 异常
这是一类特殊的类型,用于表示程序遇到了异常情况。
4.1 什么情况下会遇到异常
凡是代码无法正确执行的时候,就会抛出一个异常。
- 除数为 0 的时候,抛出
ZeroDivisionError。 - 访问的列表元素超出了列表长度,抛出
IndexError。 - 参数的类型不对,抛出
TypeError。 - 使用的变量或函数找不到,抛出
NameError。 - 找不到指定文件,抛出
FileNotFoundError。 - 网页不存在(404),抛出
HTTPError。 - 网页超时无法打开,抛出
ConnectTimeoutError。
4.2 raise 语句抛出异常
在一些情况下,如果认为当前的情况是一种异常情况,可以使用 raise 手动抛出一个异常。 这表示我们不对当前情况做任何处理,而是交给更上一层的代码块处理。
raise ValueError("Some message")
raise ValueError(("Some message", 2))异常的构造函数通常都有一个参数,用来保存一些信息,这些信息被放在了 args 成员变量中。
异常虽然有很多种类型,但不同类型之间在定义上没有太大的区别。 使用不同异常类型的目的是指示这个异常的性质。 比如当除数是 0 时,使用 ZeroDivisionError 就比 ValueError 更清晰的指出了异常的本质。
在写网络爬虫的时候,正常情况下会获得一个字典,其中有一个键名(字段)是
data,对应的键值表示要获取的数据,可以从中提取出我们需要的信息。 但是有的时候可能会发生一些错误,此时这个字段就消失了。 这时需要抛出一个ValueError的异常,表示获得的结果不对。
def get_data(response):
if "data" not in response:
raise ValueError("Field 'data' not found in response")
data = response["data"]
return len(data)response1 = {'status': 200, 'data': [{'name': '碧桂园'}, {'name': '万达'}]}
get_data(response1)2
response2 = {'status': 404, 'message': 'Page not found.'}
get_data(response2)ValueError: Field 'data' not found in response
4.3 try-except 语句捕获异常
格式
try:
# Some code
except:
# Deal with exceptiontry:
# Some code
except SomeException:
# Deal with exception
except AnotherException as e:
# Deal with exception
# using some info in e- 如果不指定异常类型,则捕获所有类型的异常
- 如果指定异常类型 A,也会捕获派生自 A 的类型的异常
- 异常类型是从上至下匹配的
循环使用
get_data函数处理下面的返回值列表,并处理的异常。 当遇到异常时,输出一句话,显示是在处理哪个response时发生的异常。
response_list = [response1, response2]for i, res in enumerate(response_list):
try:
get_data(res)
except ValueError as e:
print(f"Value error raised at response {i}: {e.args[0]}")Value error raised at response 1: Field 'data' not found in response
5 下节
模块
- 安装模块
- 常用模块
脚注
静态函数和类函数除外,暂时不考虑这种特殊情况。↩︎