博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python 面向对象编程_学习使用Python进行的面向对象编程
阅读量:2524 次
发布时间:2019-05-11

本文共 14571 字,大约阅读时间需要 48 分钟。

python 面向对象编程

在上一篇文章中,我解释了如何通过使用函数,创建模块或同时使用两者来 。 为了避免重复使用您打算多次使用的代码,函数是无价的,模块确保您可以在不同项目中使用代码。 但是模块化还有另一个组成部分:类。

如果您听说过术语“ 面向对象的编程” ,那么您可能对用途类有一些概念。 程序员倾向于将类视为虚拟对象,有时与物理世界中的某些事物具有直接相关性,而有时则将其视为某种编程概念的体现。 无论哪种方式,当您想在程序内创建“对象”以供您或该程序的其他部分进行交互时,都可以创建一个类。

没有类的模板

这是一个完全基于函数的敌人生成器实现示例:

#!/usr/bin/env python3      
import
random
def enemy
( ancestry
, gear
) :
    enemy
= ancestry
    weapon
= gear
    hp
=
random .
randrange
(
0
,
20
)
    ac
=
random .
randrange
(
0
,
20
)
   
return
[ enemy
, weapon
, hp
, ac
]
def fight
( tgt
) :
   
print
(
"You take a swing at the " + tgt
[
0
] +
"."
)
    hit
=
random .
randrange
(
0
,
20
)
   
if hit
> tgt
[
3
] :
       
print
(
"You hit the " + tgt
[
0
] +
" for " +
str
( hit
) +
" damage!"
)
        tgt
[
2
]
= tgt
[
2
] - hit
   
else :
       
print
(
"You missed."
)
foe
= enemy
(
"troll"
,
"great axe"
)
print
(
"You meet a " + foe
[
0
] +
" wielding a " + foe
[
1
]
)
print
(
"Type the a key and then RETURN to attack."
)
while
True :
    action
=
input
(
)
   
if action.
lower
(
)
==
"a" :
        fight
( foe
)
   
if foe
[
2
]
<
1 :
       
print
(
"You killed your foe!"
)
   
else :
       
print
(
"The " + foe
[
0
] +
" has " +
str
( foe
[
2
]
) +
" HP remaining"
)

敌人功能可以创建具有多个属性的敌人,例如血统,武器,生命值和防御等级。 它返回每个属性的列表,代表敌人的总数。

从某种意义上说,该代码创建了一个对象,即使它尚未使用类。 程序员称其为“敌人”是一个对象,因为函数的结果(在这种情况下为字符串和整数列表)表示游戏中的一个奇异但复杂的事物 。 也就是说,列表中的字符串和整数不是任意的:它们共同描述了一个虚拟对象。

在编写描述符的集合时,您可以使用变量,以便可以在想要产生敌人的任何时候使用它们。 有点像模板。

在示例代码中,当需要对象的属性时,将检索相应的列表项。 例如,要获取敌人的血统,代码将查看foe [0] ,获取健康点,并查看foe [2]获取健康点,依此类推。

这种方法不一定没有错。 该代码按预期运行。 您可以添加更多不同类型的敌人,可以创建敌人类型列表,并在创建敌人时从列表中随机选择,依此类推。 它运行得很好,实际上, 非常有效地使用了这一原理来近似面向对象的模型。

但是,有时对象不仅仅是属性列表。

对象的方式

在Python中,一切都是对象。 您在Python中创建的任何内容都是某些预定义模板的实例 。 甚至基本的字符串和整数都是Python 类型类的派生类。 您可以亲自看到一个交互式Python shell:

>>> foo      
=
3
>>>
type
( foo
)
<
class
'int'
>
>>> foo
=
"bar"
>>>
type
( foo
)
<
class
'str'
>

当对象由类定义时,它不仅仅是属性的集合。 Python类具有各自的功能。 从逻辑上讲这很方便,因为仅与某个特定对象类有关的动作包含在该对象的类内。

在示例代码中,斗争代码是主应用程序的功能。 对于一款简单的游戏来说,这很好用,但在复杂的游戏中,游戏世界中不仅会有玩家和敌人。 可能有城镇居民,牲畜,建筑物,森林等,它们都不需具备战斗功能。 将代码放置在敌人的战斗中意味着您的代码组织得更好; 在复杂的应用程序中,这是一个很大的优势。

此外,每个类都有权访问其自己的局部变量。 例如,敌人的健康点不是应该改变的数据,除非通过敌人阶级的某些职能。 游戏中随机出现的蝴蝶不应使敌人的生命值意外降低至0。理想情况下,即使没有阶级,也永远不会发生这种情况,但是在具有大量活动部件的复杂应用程序中,确保部件能够有效地移动是一个强大的诀窍不需要彼此互动,永远也不需要。

Python类也需要进行垃圾回收。 当不再使用类的实例时,会将其移出内存。 您可能永远不知道何时发生这种情况,但是您往往会注意到何时没有发生这种情况,因为您的应用程序占用的内存更多,运行速度也比预期的慢。 将数据集隔离到类中有助于Python跟踪正在使用的内容和不再需要的内容。

优雅的Python

这是使用针对敌人的类的简单战斗游戏:

#!/usr/bin/env python3      
import
random
class Enemy
(
) :
   
def
__init__
(
self
, ancestry
, gear
) :
       
self .
enemy
= ancestry
       
self .
weapon
= gear
       
self .
hp
=
random .
randrange
(
10
,
20
)
       
self .
ac
=
random .
randrange
(
12
,
20
)
       
self .
alive
=
True
   
def fight
(
self
, tgt
) :
       
print
(
"You take a swing at the " +
self .
enemy +
"."
)
        hit
=
random .
randrange
(
0
,
20
)
       
if
self .
alive
and hit
>
self .
ac :
           
print
(
"You hit the " +
self .
enemy +
" for " +
str
( hit
) +
" damage!"
)
           
self .
hp
=
self .
hp - hit
           
print
(
"The " +
self .
enemy +
" has " +
str
(
self .
hp
) +
" HP remaining"
)
       
else :
           
print
(
"You missed."
)
       
if
self .
hp
<
1 :
           
self .
alive
=
False
# game start
foe
= Enemy
(
"troll"
,
"great axe"
)
print
(
"You meet a " + foe.
enemy +
" wielding a " + foe.
weapon
)
# main loop
while
True :
   
   
print
(
"Type the a key and then RETURN to attack."
)
       
    action
=
input
(
)
   
if action.
lower
(
)
==
"a" :
        foe.
fight
( foe
)
               
   
if foe.
alive
==
False :
       
print
(
"You have won...this time."
)
        exit
(
)

此版本的游戏将敌人当作具有相同属性(祖先,武器,健康和防御)的对象来处理,再加上一个新属性来衡量敌人是否已被消灭,并具有战斗功能。

类的第一个功能是一个特殊功能,在Python中称为init或初始化功能。 这类似于其他语言中的 。 它创建了该类的实例,该实例可以通过其属性以及调用该类时使用的任何变量来识别(实例代码中的敌人 )。

自我和课堂实例

类的函数接受一种新的输入形式,您在类外部看不到: self 。 如果您不包括self ,那么在调用类函数时,Python将无法知道要使用该类的哪个实例。 就像在一个充满兽人的房间里说“我将与兽人战斗”,挑战一个兽人决斗。 没有人知道您指的是哪一个,因此发生了坏事。

Image of an Orc, CC-BY-SA by Buch on opengameart.org

Buch的CC-BY-SA在opengameart.org上

在类中创建的每个属性都以自我符号开头,该符号将变量标识为类的属性。 生成一个类的实例后,将自己的前缀替换为代表该实例的变量。 使用这种技术,您可以说“我将与gorblar.orc战斗”,从而在一个充满兽人的房间里挑战一个兽人与决斗。 当兽人Gorblar听到gorblar.orc时 ,他知道您指的是哪个兽人(他自己 ),因此您将获得一场公平的战斗,而不是吵架。 在Python中:

gorblar      
= Enemy
(
"orc"
,
"sword"
)
print
(
"The " + gorblar.
enemy +
" has " +
str
( gorblar.
hp
) +
" remaining."
)

无需查找敌人类型的foe [0] (或在函数示例中)或gorblar [0] ,而是检索类属性( gorblar.enemygorblar.hp或所需的任何对象的任何值)。

局部变量

如果类中的变量没有在self关键字前加上,则它是局部变量,就像在任何函数中一样。 例如,无论您做什么,都无法访问Enemy.fight类之外的hit变量:

>>>      
print
( foe.
hit
)
Traceback
( most recent call last
) :
  File
"./enclass.py"
, line
38
,
in
< module
>
   
print
( foe.
hit
)
AttributeError :
'Enemy'
object has no attribute
'hit'
>>>
print
( foe.
fight .
hit
)
Traceback
( most recent call last
) :
  File
"./enclass.py"
, line
38
,
in
< module
>
   
print
( foe.
fight .
hit
)
AttributeError :
'function'
object has no attribute
'hit'

hit变量包含在Enemy类中,并且只有“生存”足够长的时间才能在战斗中发挥作用。

更多模块化

本示例使用与主应用程序相同的文本文档中的类。 在复杂的游戏中,将每个类视为自己独立的应用程序要容易得多。 当多个开发人员在同一个应用程序上工作时,您会看到这种情况:一个开发人员在一个类上工作,另一个开发人员在主程序上工作,并且只要他们就类必须具有的属性进行沟通,这两个代码库就可以并行开发。

为了使该示例游戏模块化,将其分为两个文件:一个用于主应用程序,一个用于类。 如果它是一个更复杂的应用程序,则每个类可能有一个文件,或者每个类的逻辑组可能有一个文件(例如,用于建筑物的文件,用于自然环境的文件,用于敌人和NPC的文件等等)。

将仅包含Enemy类的一个文件另存为敌人.py ,将包含其他所有内容的另一个另存main.py。

这是敌人。py

import      
random
class Enemy
(
) :
   
def
__init__
(
self
, ancestry
, gear
) :
       
self .
enemy
= ancestry
       
self .
weapon
= gear
       
self .
hp
=
random .
randrange
(
10
,
20
)
       
self .
stg
=
random .
randrange
(
0
,
20
)
       
self .
ac
=
random .
randrange
(
0
,
20
)
       
self .
alive
=
True
   
def fight
(
self
, tgt
) :
       
print
(
"You take a swing at the " +
self .
enemy +
"."
)
        hit
=
random .
randrange
(
0
,
20
)
       
if
self .
alive
and hit
>
self .
ac :
           
print
(
"You hit the " +
self .
enemy +
" for " +
str
( hit
) +
" damage!"
)
           
self .
hp
=
self .
hp - hit
           
print
(
"The " +
self .
enemy +
" has " +
str
(
self .
hp
) +
" HP remaining"
)
       
else :
           
print
(
"You missed."
)
       
if
self .
hp
<
1 :
           
self .
alive
=
False

这是main.py

#!/usr/bin/env python3      
import enemy
as en
# game start
foe
= en.
Enemy
(
"troll"
,
"great axe"
)
print
(
"You meet a " + foe.
enemy +
" wielding a " + foe.
weapon
)
# main loop
while
True :
   
   
print
(
"Type the a key and then RETURN to attack."
)
    action
=
input
(
)
   
if action.
lower
(
)
==
"a" :
        foe.
fight
( foe
)
   
if foe.
alive
==
False :
       
print
(
"You have won...this time."
)
        exit
(
)

导入模块hero.py的过程非常具体,它使用一条声明,将类文件作为其文件名而不带.py扩展名,然后是您选择的命名空间指示符(例如, 将敌人导入为en )。 该标识符是您在调用类时在代码中使用的标识符。 不仅要使用Enemy()还要在类的前面加上要导入的内容的名称,例如en.Enemy

所有这些文件名都是完全任意的,尽管在原理上并不罕见。 命名应用程序中用作中心集线器main.py的部分是一种常见的约定,充满类的文件通常以小写命名,其中包含所有类,每个类均以大写字母开头。 是否遵循这些约定不会影响应用程序的运行方式,但是它确实使有经验的Python程序员可以更轻松地快速了解应用程序的工作方式。

在结构代码方面有一定的灵活性。 例如,使用代码示例,两个文件必须位于同一目录中。 如果要仅将类打包为模块,则必须创建一个名为mybad的目录, 并将类移入其中。 在main.py中 ,您的import语句稍有变化:

from mybad import enemy as en

两种系统都产生相同的结果,但是如果您创建的类足够通用,以至于您认为其他开发人员可以在他们的项目中使用它们,则后者是最好的。

无论选择哪种,都启动游戏的模块化版本:

$ python3 .      
/ main.py
You meet a troll wielding a great axe
Type the a key and
then RETURN to attack.
a
You take a swing at the troll.
You missed.
Type the a key and
then RETURN to attack.
a
You take a swing at the troll.
You hit the troll
for
8 damage
!
The troll has
4 HP remaining
Type the a key and
then RETURN to attack.
a
You take a swing at the troll.
You hit the troll
for
11 damage
!
The troll has
-7 HP remaining
You have won...this time.

游戏工作。 它是模块化的。 现在,您知道了应用程序面向对象的含义。 但最重要的是,您知道在挑战兽人对决时要具体。

翻译自:

python 面向对象编程

转载地址:http://mpbzd.baihongyu.com/

你可能感兴趣的文章
阶段1 语言基础+高级_1-3-Java语言高级_08-JDK8新特性_第4节 方法引用_5_方法引用_通过this引用本类的成员...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_08-JDK8新特性_第4节 方法引用_7方法引用_数组的构造器引用...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_08-JDK8新特性_第4节 方法引用_3_方法引用_通过类名引用静态成员...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第1节 基础加强_3_Junit_使用步骤...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_08-JDK8新特性_第4节 方法引用_6_方法引用_类的构造器(构造方法)引用...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_5_反射_概述
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第1节 基础加强_2_Junit_测试概述...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_7_反射_Class对象功能概述...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第1节 基础加强_4_Junit_@Before&@After...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_9_反射_Class对象功能_获取Constructor...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_6_反射_获取字节码Class对象的三种方式...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_11_反射_案例
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_8_反射_Class对象功能_获取Field...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_10_反射_Class对象功能_获取Method成员方法...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_13_注解_JDK内置注解...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_12_注解_概念
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_15_注解_自定义注解_属性定义...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_14_注解_自定义注解_格式&本质...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_17_注解_解析注解...
查看>>
阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_16_注解_自定义注解_元注解...
查看>>