高级 Python 编程学习指南
1. 模块与类的导入
在创建
Tank
实例的文件中,需要修改导入语句。例如,从
combatsim
包的
tank
模块导入
Tank
类:
from combatsim.tank import Tank
其语法遵循
from package.module import name
的模式。还可以从
vehicle
模块仅导入
Ground_Vehicle
类:
from vehicle import Ground_Vehicle
仅导入绝对需要的类,比使用
import *
更易于调试和维护代码。
2. 添加派生类
以
Tank
类为例,可以为其他类型的地面车辆添加派生类,如装甲运兵车(APC)。步骤如下:
1. 在
combatsim
包中创建一个新模块来定义该类。
2. 典型的 APC 与坦克类似,有两条履带、装甲车体和炮塔,但还能搭载步兵,且火炮通常是小口径速射炮。
3. 需要在
components.py
文件中添加组件类定义,以表示自动火炮和步兵货物。
4. 完成 APC 类后,还可以尝试添加货运卡车类。由于卡车有四个或六个轮子而非履带,需要添加一个组件来表示轮子。
3. 类和实例使用中的潜在陷阱
为进一步说明 Python 类的工作原理,我们构建一些简单的测试类。
3.1 类属性与实例属性的使用
以下是一个示例代码:
class Test():
class_list = []
def __init__(self):
self.instance_list = []
instance_1 = Test()
instance_2 = Test()
instance_1.instance_list.append(1)
instance_2.instance_list.append(2)
print("Instance 1 instance_list:" + str(instance_1.instance_list))
print("Instance 2 instance_list:" + str(instance_2.instance_list))
print("Appending values to class_list:")
instance_1.class_list.append(3)
instance_2.class_list.append(4)
print("Instance 1 class_list:" + str(instance_1.class_list))
print("Instance 2 class_list:" + str(instance_2.class_list))
print("Adding new attributes:")
instance_1.new_list = [5,6]
instance_2.new_list = [7,8]
print("Instance 1 new list:" + str(instance_1.new_list))
print("Instance 2 new list:" + str(instance_2.new_list))
输出结果如下:
Instance 1 instance_list:[1]
Instance 2 instance_list:[2]
Appending values to class_list:
Instance 1 class_list:[3, 4]
Instance 2 class_list:[3, 4]
Adding new attributes:
Instance 1 new list:[5, 6]
Instance 2 new list:[7, 8]
在这个例子中,
instance_list
是实例属性,每个实例都有自己的副本;而
class_list
是类属性,所有实例共享同一个副本。
3.2 更多关于类属性与实例属性
以下代码展示了更多细节:
class Test2():
value1 = 5
def method1(self):
return 'Old Method '
instance_1 = Test2()
instance_2 = Test2()
print("Instance 1, value1 = " + str(instance_1.value1))
print("Instance 2, value1 = " + str(instance_2.value1))
print("Changing value1:")
instance_1.value1 = 6
print("Instance 1, value1 = " + str(instance_1.value1))
print("Instance 2, value1 = " + str(instance_2.value1))
print("Instance 1, method1: " + instance_1.method1())
print("Instance 2, method1: " + instance_2.method1())
def new_method():
return 'New Method'
print("Adding a new method:")
instance_1.method1 = new_method
print("Instance 1, method1: " + instance_1.method1())
print("Instance 2, method1: " + instance_2.method1())
输出如下:
Instance 1, value1 = 5
Instance 2, value1 = 5
Changing value1:
Instance 1, value1 = 6
Instance 2, value1 = 5
Instance 1, method1: Old Method
Instance 2, method1: Old Method
Adding a new method:
Instance 1, method1: New Method
Instance 2, method1: Old Method
当执行
instance_1.value1 = 6
时,实际上是用一个实例属性替换了类属性
value1
。同时,类方法也遵循类似的规则,所有实例共享相同的方法,但可以通过定义新函数来替换现有方法,新方法仅附加到指定实例。
4. 创建空类和函数
有两种情况适合定义没有属性或方法的类:一是在编码模块或包的初始阶段,将类或函数作为“占位符”或“存根”,后续再填充内容;二是需要一个类像 C 语言中的结构体或 Pascal 语言中的记录一样工作。
以下是示例代码:
# Create an empty class to use as a data structure
class Struct():
pass
# Use pass to define empty methods
class VTOL():
"""Class to represent airborne vehicles with VTOL
(Vertical Take-Off/Landing) capabilities, such as
helicopters, tilt-rotors, and Harrier jump jets.
"""
def __init__(self):
pass
def move(self, horizontal_angle, vertical_angle, distance):
pass
data_container = Struct()
data_container.name = "String data"
data_container.count = 14
data_container.remainder = 0.1037
print(data_container.name)
print(data_container.count)
print(data_container.remainder)
输出结果:
String data
14
0.1037
使用
pass
关键字可以定义空类和空函数,避免 Python 解释器报错。
5. 优雅地处理错误
程序中难免会出现错误,一般分为两类:程序逻辑设计错误(bug)和代码使用不当导致的错误。Python 中的运行时错误称为异常,与语法错误不同,异常不会阻止程序运行。
5.1 异常示例
以下是两个常见异常示例:
-
TypeError
异常:
sage: sin("one")
-----------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/cfinch/Documents/Articles/Sage Math/Chapters/Chapter 9/
Code/<ipython console> in <module>()
/Applications/sage/local/lib/python2.6/site-packages/sage/symbolic/
function.so in sage.symbolic.function.GinacFunction.__call__ (sage/
symbolic/function.cpp:6572)()
/Applications/sage/local/lib/python2.6/site-packages/sage/symbolic/
function.so in sage.symbolic.function.Function.__call__ (sage/symbolic/
function.cpp:4336)()
TypeError: cannot coerce arguments: no canonical coercion from <type
'str'> to Symbolic Ring
-
ZeroDivisionError异常:
sage: 1/0
-----------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
/Users/cfinch/Documents/Articles/Sage Math/Chapters/Chapter 9/
Code/<ipython console> in <module>()
/Applications/sage/local/lib/python2.6/site-packages/sage/structure/
element.so in sage.structure.element.RingElement.__div__ (sage/structure/
element.c:11973)()
/Applications/sage/local/lib/python2.6/site-packages/sage/rings/integer.
so in sage.rings.integer.Integer._div_ (sage/rings/integer.c:11163)()
/Applications/sage/local/lib/python2.6/site-packages/sage/rings/integer_
ring.so in sage.rings.integer_ring.IntegerRing_class._div (sage/rings/
integer_ring.c:5022)()
ZeroDivisionError: Rational division by zero
5.2 异常的抛出与处理
以战斗模拟为例,修改
vehicle.py
文件中的
move
方法,使其在遇到无效参数时抛出异常:
import sage.all
class Ground_Vehicle():
"""Base class for all ground vehicles"""
def __init__(self, position):
"""Create a vehicle instance
position (x,y) tuple of coordinates
"""
# Intialize position
self._x = position[0]
self._y = position[1]
def move(self, direction, distance):
"""Move the vehicle.
Arguments:
direction floating-point number representing
the compass angle of movement in degrees. North is
0,
east is 90, south is 180, and west is 270.
0 <= direction < 360
distance distance to move (in meters)
"""
if (direction < 0) or (direction >= 360):
raise ValueError("Error: Direction must be greater \
than or equal to zero and less than 360.")
if distance < 0:
raise ValueError("Error: Distance must be >= 0.")
self._x += sage.all.n(distance * sage.all.cos(direction *
sage.all.pi / 180))
self._y += sage.all.n(distance * sage.all.sin(direction *
sage.all.pi / 180))
def get_position(self):
"""Returns a tuple with the (x,y) coordinates of the tank's
current location.
"""
return (float(self._x), float(self._y))
在另一个 Python 文件中使用该类并处理异常:
from combatsim import tank
# Define parameters for the tank
armor_values = {'front' : 100, 'side' : 50, 'rear' : 25,
'turret' : 75}
main_gun_damage = 50
initial_position = (0.0, 0.0)
# Create a tank object
tank_1 = tank.Tank(armor_values, main_gun_damage, initial_position)
pos = tank_1.get_position()
print("Initial position: x = {0:.2f}m y = {1:.2f}m".format(pos[0],
pos[1]))
# Move 10m north
try:
tank_1.move(0.0, 10.0)
except ValueError as error:
print(error.args[0])
else:
pos = tank_1.get_position()
print("Current position: x = {0:.2f}m y = {1:.2f}
m".format(pos[0], pos[1]))
# Try invalid direction
try:
tank_1.move(361, 10.0)
except ValueError as error:
print(error.args[0])
else:
pos = tank_1.get_position()
print("Current position: x = {0:.2f}m y = {1:.2f}
m".format(pos[0], pos[1]))
# Try invalid distance
try:
tank_1.move(90, -1.0)
except ValueError as error:
print(error.args[0])
else:
pos = tank_1.get_position()
print("Current position: x = {0:.2f}m y = {1:.2f}
m".format(pos[0], pos[1]))
结果如下:
Initial position: x = 0.00m y = 0.00m
Current position: x = 10.00m y = 0.00m
Error: Direction must be greater than or equal to zero and less than 360.
Error: Distance must be >= 0.
异常抛出的语法为
raise ExceptionType(args)
,处理异常的一般语法如下:
try:
statement 1
statement 2
...
except ExceptionType1 as error:
handle exception
except ExceptionType2:
handle exception
else:
statements
finally:
clean up
使用异常处理可以使代码更灵活,并且在遇到异常后程序仍能继续运行。
6. Python 内置异常类型
Python 内置了许多异常类型,以下是部分常见的异常:
| 异常类型 | 描述 |
| ---- | ---- |
|
AssertionError
| 断言语句失败时触发 |
|
KeyError
| 字典中查找不存在的键时触发 |
|
RuntimeError
| 一般运行时错误 |
|
ValueError
| 传入无效参数时触发 |
|
ZeroDivisionError
| 除数为零时触发 |
7. 自定义异常类型
可以通过定义从现有异常类型派生的异常类来创建自定义异常类型。
7.1 创建自定义异常类
在
combatsim
目录下创建
exceptions.py
文件,定义以下异常类:
class CombatsimError(Exception):
"""Base class for exceptions generated by combatsim"""
def __init__(self, value):
"""Create a CombatsimError exception.
Arguments:
value String describing the error
"""
Exception.__init__(self, value)
class MoveError(CombatsimError):
def __init__(self, value):
CombatsimError.__init__(self,value)
class ShootError(CombatsimError):
def __init__(self, value):
CombatsimError.__init__(self,value)
修改
vehicle.py
文件:
import sage.all
from exceptions import *
class Ground_Vehicle():
"""Base class for all ground vehicles"""
def __init__(self, position):
"""Create a vehicle instance
position (x,y) tuple of coordinates
"""
# Intialize position
self._x = position[0]
self._y = position[1]
def move(self, direction, distance):
"""Move the vehicle.
Arguments:
direction floating-point number representing
the compass angle of movement in degrees. North is
0,
east is 90, south is 180, and west is 270.
0 <= direction < 360
distance distance to move (in meters)
"""
if (direction < 0) or (direction >= 360):
raise MoveError("Error: Direction must be greater \
than or equal to zero and less than 360.")
if distance < 0:
raise MoveError("Error: Distance must be >= 0.")
self._x += sage.all.n(distance * sage.all.cos(direction *
sage.all.pi / 180))
self._y += sage.all.n(distance * sage.all.sin(direction *
sage.all.pi / 180))
def get_position(self):
"""Returns a tuple with the (x,y) coordinates of the tank's
current location.
"""
return (float(self._x), float(self._y))
修改
tank.py
文件并添加
fire
方法:
from components import *
from exceptions import *
from vehicle import Ground_Vehicle
class Tank(Ground_Vehicle):
"""Model of an armored fighting vehicle."""
def __init__(self, armor_values, cannon_damage_value, position):
"""Constructs a tank instance
Arguments:
armor_values A dictionary of armor values
of the form:
{'front' : 100, 'side' : 50, 'rear' : 25,
'turret' : 75}
cannon_damage_value Integer that represents
the damage inflicted by the main gun
position (x,y) tuple of coordinates
"""
# Initialize armor
self._frontal_armor = armor_values['front']
self._left_armor = armor_values['side']
self._right_armor = armor_values['side']
self._rear_armor = armor_values['rear']
self._turret_armor = armor_values['turret']
# Add tank components
main_gun = Cannon(cannon_damage_value)
self._turret = Turret(main_gun)
self._left_track = Track()
self._right_track = Track()
Ground_Vehicle.__init__(self, position)
def __str__(self):
import os
ls = os.linesep
description = 'Tank parameters:' + ls
description += ' Armor values:' + ls
description += ' Front:' + str(self._frontal_armor) + ls
description += ' Left:' + str(self._left_armor) + ls
description += ' Right:' + str(self._right_armor) + ls
description += ' Rear:' + str(self._rear_armor) + ls
description += ' Turret:' + str(self._turret_armor) + ls
description += ' Weapons:' + ls
description += ' Main cannon:' + ls
description += ' ' + str(self._turret) + ls
return description
def fire(self, direction, elevation):
""" Fire the cannon.
Arguments:
direction degrees, 0 <= direction < 360
elevation degrees, 0 <= direction < 90
"""
if (direction < 0) or (direction >= 360):
raise ShootError("Error: Firing direction must be \
greater than or equal to zero and less than 360.")
if (elevation < 0) or (elevation >= 90):
raise ShootError("Error: Firing elevation must be \
greater than or equal to zero and less than 90.")
print "Bang!"
在新的 Python 文件中使用自定义异常:
from combatsim import tank
import combatsim.exceptions as ex
# Define parameters for the tank
armor_values = {'front' : 100, 'side' : 50, 'rear' : 25,
'turret' : 75}
main_gun_damage = 50
initial_position = (0.0, 0.0)
# Create a tank object
tank_1 = tank.Tank(armor_values, main_gun_damage, initial_position)
pos = tank_1.get_position()
print("Initial position: x = {0:.2f}m y = {1:.2f}m".format(pos[0],
pos[1]))
# Move 10m north
try:
tank_1.move(0.0, 10.0)
except ex.MoveError as error:
print(error)
else:
pos = tank_1.get_position()
print("Current position: x = {0:.2f}m y = {1:.2f}
m".format(pos[0], pos[1]))
# Valid arguments to fire method
try:
tank_1.fire(325,24)
except ex.ShootError as error:
print(error.args[0])
# Invalid arguments to fire method
try:
tank_1.fire(325,-1)
except ex.CombatsimError as error:
print(error.args[0])
# Invalid arguments to move and fire methods
try:
tank_1.move(-1,1)
tank_1.fire(325,97)
except (ValueError, ex.ShootError) as error:
print("Firing error.")
print(error.args[0])
except ex.MoveError as error:
print("Movement error:")
print(error.args[0])
结果如下:
Initial position: x = 0.00m y = 0.00m
Current position: x = 10.00m y = 0.00m
Bang!
Error: Firing elevation must be greater than or equal to zero and less
than 90.
Movement error:
Error: Direction must be greater than or equal to zero and less than 360.
自定义异常类的使用可以更好地区分程序中不同类型的错误。
通过以上内容的学习,我们可以更深入地理解 Python 中类和异常的使用,提高代码的健壮性和可维护性。在实际编程中,合理运用这些知识可以帮助我们更好地处理各种情况,避免程序出现意外错误。
高级 Python 编程学习指南(续)
8. 异常处理流程总结
异常处理在 Python 编程中至关重要,下面通过 mermaid 流程图来展示异常处理的整体流程:
graph TD;
A[开始执行代码] --> B{是否进入 try 块};
B -- 是 --> C{执行 try 块内代码};
C -- 无异常 --> D[执行 else 块(如果有)];
D --> E[执行 finally 块(如果有)];
E --> F[结束];
C -- 有异常 --> G{是否匹配 except 块};
G -- 是 --> H[执行匹配的 except 块];
H --> E;
G -- 否 --> I[异常未处理,程序终止];
B -- 否 --> F;
从流程图可以清晰地看到,当代码进入
try
块后,若没有异常发生,会执行
else
块(如果有),然后执行
finally
块;若有异常发生,会检查是否有匹配的
except
块,若有则执行该块,最后执行
finally
块;若没有匹配的
except
块,程序将终止。
9. 综合案例分析
我们结合之前的战斗模拟案例,进一步分析如何运用所学知识构建一个完整的程序。在这个案例中,我们有
Ground_Vehicle
类和
Tank
类,并且定义了自定义异常。下面是一个更详细的使用步骤:
9.1 项目结构
项目结构如下:
combatsim/
├── __init__.py
├── tank.py
├── vehicle.py
├── components.py
└── exceptions.py
main.py
9.2 代码实现
-
exceptions.py:
class CombatsimError(Exception):
"""Base class for exceptions generated by combatsim"""
def __init__(self, value):
"""Create a CombatsimError exception.
Arguments:
value String describing the error
"""
Exception.__init__(self, value)
class MoveError(CombatsimError):
def __init__(self, value):
CombatsimError.__init__(self,value)
class ShootError(CombatsimError):
def __init__(self, value):
CombatsimError.__init__(self,value)
-
components.py:
class Cannon:
def __init__(self, damage):
self.damage = damage
def __str__(self):
return f"Cannon with damage {self.damage}"
class Turret:
def __init__(self, main_gun):
self.main_gun = main_gun
def __str__(self):
return str(self.main_gun)
class Track:
def __str__(self):
return "Track"
-
vehicle.py:
import sage.all
from exceptions import *
class Ground_Vehicle():
"""Base class for all ground vehicles"""
def __init__(self, position):
"""Create a vehicle instance
position (x,y) tuple of coordinates
"""
# Intialize position
self._x = position[0]
self._y = position[1]
def move(self, direction, distance):
"""Move the vehicle.
Arguments:
direction floating-point number representing
the compass angle of movement in degrees. North is
0,
east is 90, south is 180, and west is 270.
0 <= direction < 360
distance distance to move (in meters)
"""
if (direction < 0) or (direction >= 360):
raise MoveError("Error: Direction must be greater \
than or equal to zero and less than 360.")
if distance < 0:
raise MoveError("Error: Distance must be >= 0.")
self._x += sage.all.n(distance * sage.all.cos(direction *
sage.all.pi / 180))
self._y += sage.all.n(distance * sage.all.sin(direction *
sage.all.pi / 180))
def get_position(self):
"""Returns a tuple with the (x,y) coordinates of the tank's
current location.
"""
return (float(self._x), float(self._y))
-
tank.py:
from components import *
from exceptions import *
from vehicle import Ground_Vehicle
class Tank(Ground_Vehicle):
"""Model of an armored fighting vehicle."""
def __init__(self, armor_values, cannon_damage_value, position):
"""Constructs a tank instance
Arguments:
armor_values A dictionary of armor values
of the form:
{'front' : 100, 'side' : 50, 'rear' : 25,
'turret' : 75}
cannon_damage_value Integer that represents
the damage inflicted by the main gun
position (x,y) tuple of coordinates
"""
# Initialize armor
self._frontal_armor = armor_values['front']
self._left_armor = armor_values['side']
self._right_armor = armor_values['side']
self._rear_armor = armor_values['rear']
self._turret_armor = armor_values['turret']
# Add tank components
main_gun = Cannon(cannon_damage_value)
self._turret = Turret(main_gun)
self._left_track = Track()
self._right_track = Track()
Ground_Vehicle.__init__(self, position)
def __str__(self):
import os
ls = os.linesep
description = 'Tank parameters:' + ls
description += ' Armor values:' + ls
description += ' Front:' + str(self._frontal_armor) + ls
description += ' Left:' + str(self._left_armor) + ls
description += ' Right:' + str(self._right_armor) + ls
description += ' Rear:' + str(self._rear_armor) + ls
description += ' Turret:' + str(self._turret_armor) + ls
description += ' Weapons:' + ls
description += ' Main cannon:' + ls
description += ' ' + str(self._turret) + ls
return description
def fire(self, direction, elevation):
""" Fire the cannon.
Arguments:
direction degrees, 0 <= direction < 360
elevation degrees, 0 <= direction < 90
"""
if (direction < 0) or (direction >= 360):
raise ShootError("Error: Firing direction must be \
greater than or equal to zero and less than 360.")
if (elevation < 0) or (elevation >= 90):
raise ShootError("Error: Firing elevation must be \
greater than or equal to zero and less than 90.")
print("Bang!")
-
main.py:
from combatsim import tank
import combatsim.exceptions as ex
# Define parameters for the tank
armor_values = {'front' : 100, 'side' : 50, 'rear' : 25,
'turret' : 75}
main_gun_damage = 50
initial_position = (0.0, 0.0)
# Create a tank object
tank_1 = tank.Tank(armor_values, main_gun_damage, initial_position)
pos = tank_1.get_position()
print("Initial position: x = {0:.2f}m y = {1:.2f}m".format(pos[0],
pos[1]))
# Move 10m north
try:
tank_1.move(0.0, 10.0)
except ex.MoveError as error:
print(error)
else:
pos = tank_1.get_position()
print("Current position: x = {0:.2f}m y = {1:.2f}m".format(pos[0], pos[1]))
# Valid arguments to fire method
try:
tank_1.fire(325,24)
except ex.ShootError as error:
print(error.args[0])
# Invalid arguments to fire method
try:
tank_1.fire(325,-1)
except ex.CombatsimError as error:
print(error.args[0])
# Invalid arguments to move and fire methods
try:
tank_1.move(-1,1)
tank_1.fire(325,97)
except (ValueError, ex.ShootError) as error:
print("Firing error.")
print(error.args[0])
except ex.MoveError as error:
print("Movement error:")
print(error.args[0])
9.3 代码分析
在这个案例中,我们通过模块化的设计,将不同的功能封装在不同的模块中。
exceptions.py
负责定义自定义异常,
components.py
定义了坦克的组件类,
vehicle.py
定义了地面车辆的基类,
tank.py
定义了坦克类,
main.py
是程序的入口,负责创建坦克对象并调用其方法,同时处理可能出现的异常。
通过自定义异常,我们可以更清晰地区分不同类型的错误,例如
MoveError
用于处理移动相关的错误,
ShootError
用于处理射击相关的错误。在
main.py
中,我们使用
try - except
语句来捕获和处理这些异常,确保程序在遇到错误时能够优雅地处理,而不是直接崩溃。
10. 总结
通过本文的学习,我们深入了解了 Python 中的模块导入、类和实例属性、空类和函数的使用、异常处理等高级编程知识。以下是本文的重点总结:
-
模块导入
:使用
from package.module import name
的方式导入特定的类或函数,避免使用
import *
以提高代码的可维护性。
-
类和实例属性
:类属性由所有实例共享,实例属性每个实例都有自己的副本。修改实例属性时可能会覆盖类属性,需要注意。
-
空类和函数
:使用
pass
关键字可以定义空类和空函数,在编码初期作为占位符使用,或者创建类似结构体的数据结构。
-
异常处理
:Python 中的异常分为内置异常和自定义异常。使用
try - except - else - finally
语句可以优雅地处理异常,提高程序的健壮性。自定义异常可以更好地区分不同类型的错误,方便代码的调试和维护。
在实际编程中,我们应该充分运用这些知识,合理设计类和模块,妥善处理异常,以构建出更加健壮、可维护的 Python 程序。
超级会员免费看
5万+

被折叠的 条评论
为什么被折叠?



