python 面向对象编程_学习使用Python进行的面向对象编程

python 面向对象编程

在上一篇文章中,我解释了如何通过使用函数,创建模块或同时使用两者来使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]获取健康点,依此类推。

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

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

对象的方式

在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.

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

翻译自: https://opensource.com/article/19/7/get-modular-python-classes

python 面向对象编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值