Skip to content

实例对象

Python 的类

Python 中可用的原始数据结构(如数字,字符串和列表)旨在分别表示简单的事物,例如进行简单的数学运算,动物的名称和你喜欢的颜色。

如果你想代表更复杂的东西怎么办?

例如对于动物,我们可以创建一个 Animal() 类来跟踪动物的属性,如名称和年龄。

重要的是要注意一个类只提供结构 ——它是应该如何定义某个东西的模板,但它实际上并不提供任何真实的内容。该 Animal() 可以指定姓名和年龄是必要的界定动物,但它实际上并不会说出什么特定动物的姓名或年龄。

类和对象的概念

对象 是面向对象编程的两个核心概念

是对一群具有相同 特征 或者 行为 的事物的一个统称,是抽象的。

  • 特征 被称为 属性
  • 行为 被称为 方法

类和对象的关系

  • 是模板,对象 是根据 这个模板创建出来的,应该先有类,再有对象
  • 只有一个,而 对象 可以有很多个
    • 不同的对象 之间 属性 可能会各不相同
    • 中定义了什么 属性和方法对象 中就有什么属性和方法

注意

例如 人(类) 是一个类对象, 人(类)会有一些特征,例如身高、体重,还有一些行为,例如吃饭、睡觉。

但是人类没有具体的属性与特征,例如 人(类) 的年龄为 18 岁,一看就知道是个病句。

但是可以说 正心(人的具体某一个实例对象) 年年 18 岁(一听就知道在开玩笑)。

实例对象

实例对象是指有类对象初始化之后得到的对象。

初始化方法

当使用类对象创建实例对象时,__init__() 初始化方法就会被自动调用。初始化既是对象生命周期的开始,也是非常重要的一个步骤, 每个对象都必须正确地执行了初始化才能够正常地工作。

通过实现 __init__() 方法来初始化一个对象。每当创建一个对象,Python 会先创建一个空对象,然后自动调用该对象的 __init__() 函数,完成初始化操作。

python
class Person:

    def __init__(self):
        print("这是一个初始化方法")


# 使用 类名 + () 创建对象的时候,会自动调
zx = Person()

初始化属性

一个对象是一系列功能的集合,包括了方法和属性。object 类的默认行为包括设置、获取和删除属性。可以通过修改这些默认行为来决定对象中哪些属性是可用的。

如果希望在创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行改造

  1. 修改 __init__ 方法的接收参数
  2. 在方法内部使用 self.属性 = 形参 接收外部传递的参数
  3. 定义属性之后,再使用 Person 类创建的对象,都会拥有该属性
  4. 在创建对象时,使用 类名(属性1, 属性2...) 调用
python
def __init__(self, name, high, weight):
    """初始化方法"""
    self.name = name
    self.high = high
    self.weight = weight

当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:

对象变量 = 类名()

完整代码

例如在玩游戏时,我们创建人物对象一般会需要人物的明显与年龄等特这个,我们可以用代码来模拟整个创建过程

python
"""  
定义一个人(Person)类  
    人在出生时就会有名字、身高、体重等特征  
    人在刚出生是会吃、喝、哭等行为  
"""


class Person(object):

    def __init__(self, name, age):
        """初始化方法"""
        self.name = name
        self.age = age

    def hello(self):
        print('我的名字是:{},已经:{}岁了'.format(self.name, self.age))


zx = Person('正心', 18)
# 获取属性  
print(zx.name)
print(zx.age)

# 在外部修改属性  
zx.age = 18

# 再外部新增属性  
zx.gender = '男'

zx.hello()

案例-实例对象

txt
"""  
创建一个游戏英雄类(Hero)  
  
分别有以下属性  
    名字(name),武器(weapon),装备(equipment),血量(blood)  
  
每个英雄类都有游戏技能,分别为(行为)  
  攻击(attack),对被攻击人造成对等的攻击力伤害  
  
创建两个英雄  
    '黄忠', '弓箭', ['头盔', '靴子'], 100  
    '刘备', '剑', ['头盔', '盔甲'], 100  
"""
参考代码
python
# Hero 类对象  
class Hero:
    def __init__(self, name, weapon, equipment, blood):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.blood = blood

    def attack(self):
        return f'{self.name} 发动了攻击'

    # 使用类对象创建一个实例对象  


# 实例对象是具体到某一个人, 类对象是某一类事物的统称  
hero1 = Hero('黄忠', '弓箭', ['头盔', '靴子'], 100)
hero2 = Hero('刘备', '剑', ['头盔', '盔甲'], 100)

print(hero1.name, hero1.weapon, hero1.equipment, hero1.attack())
print(hero1.equipment)
print(hero1.equipment[0])
print(hero1.equipment[1])

__init__ 方法(了解)

对象的生命周期主要包括了创建、初始化和销毁。后面课程会详细讨论对象的创建和销毁,本章专注于对象的初始化。

object 作为所有类的基类,已经为 __init__() 方法提供了默认实现,一般情况下不需要重写这个函数。如果没有对它进行重写,那么在创建对象时将不会产生其他变量的实例。在某些情况下,这种默认行为是可以接受的。

对于继承自 object 的子类,总可以对它的属性进行扩展。例如,对于下面这个类,实例化就不对函数(tom )所需要的变量(nameage )进行初始化。

python
class Person(object):

    # def __init__(self):
    #     pass

    def hello(self):
        print('我的名字是:{},已经:{}岁了'.format(self.name, self.age))

Person 类的 hello 函数在返回值时使用了两个属性,可并没有在任何地方对其赋值。在 Python 中,这种看似奇怪的调用尚未赋值属性的操作却是合法的。

下面这段代码演示如何使用刚定义的 Person 类。

python
>>> tom = Person()
>>> tom.name = '汤姆'
>>> tom.age = '18'
>>> tom.hello()
我的名字是:汤姆,已经:18岁了

虽然这种 延迟赋值 的实现方式在 Python 中是合法的,但是却给调用者带来了潜在的困惑,因此要尽量避免这样的用法。

然而,这样的设计看似又提供了灵活性,意味着在 __init__() 方法被调用时不必为所有的属性赋值。这看似是不错的选择,一个可选属性即可以看作是某子类中的成员,且无须对这个子类进行显式地定义就可以完成对原生机制的扩展。然而这种多态机制不但给程序带来了隐藏的不确定性,也会相应产生很多令人费解的 if 语句。

因此,延迟初始化属性的设计在某种情形下可能会有用,可是这样也可能会导致非常糟糕的设计。

在 Zen of python poem 一书中曾提出过这样的建议:

“显式而非隐式”。 对于每个__init__() 方法,都应当显式地指定要初始化的变量。

self 指代实例对象

在类封装的方法内部,self 就表示当前调用方法的实例对象。 由哪一个对象调用实例方法,方法内的 self 就是哪一个对象的引用。 在方法内部可以通过 self.属性名 访问实例对象的属性,也可以通过 self.方法名 访问实例对象的方法。

python
class Hero:
    def __init__(self, name, weapon, equipment, blood):
        self.name = name  # 把传进来的参数绑定到后面实例化的对象上  
        self.weapon = weapon
        self.equipment = equipment
        self.blood = blood

    # self 指代的就是实例化的一个一个对象  
    # 实例方法, 类模板里面的方法, 默认都需要接收一个self参数  
    # 接收 self 参数的都是实例方法, 被绑定在实例对象上面  
    def return_self(self):
        return self


hero1 = Hero('黄忠', '弓箭', ['头盔', '靴子'], 100)
attr = hero1.return_self()
print(id(hero1))
print(id(attr))

hero2 = Hero('刘备', '剑', ['头盔', '盔甲'], 100)
attr2 = hero2.return_self()
print(id(hero2))
print(id(attr2))
  • 在类的外部,通过 变量名. 访问对象的属性和方法
  • 在类封装的方法中,通过 self. 访问对象的属性和方法

带参数的实例方法

实例对象的方法与普通函数之间的区别在于第一个参数会接收 self 实例对象。其余的用法都是类似的

txt
"""  
创建一个游戏英雄类,  
  
分别有以下属性  
    名字(name),武器(weapon),装备(equipment),血量(blood)  
  
每个英雄类都有游戏技能,分别为(行为)  
  攻击(attack)与逃跑(run)  
  
创建两个英雄  
    '黄忠', '弓箭', ['头盔', '靴子'], 100  
    '刘备', '剑', ['头盔', '盔甲'], 100  
  
黄忠攻击刘备,刘备血量 -10
"""
参考代码
python
class Hero:
    def __init__(self, name, weapon, equipment, blood):
        self.name = name
        self.weapon = weapon
        self.equipment = equipment
        self.blood = blood

    def attack(self, hero=None):
        if hero:
            print(self.name, '攻击了', hero.name)
            hero.blood -= 10
            print(hero.name, '血量还剩下', hero.blood)

        else:
            print(self.name, '正在发动攻击')

    def run(self):
        print(self.name, '逃跑了')

    def return_data(self):
        # 把重要的数据保存  
        return self.name, self.weapon, self.equipment, self.blood


hz = Hero('黄忠', '弓箭', ['头盔', '靴子'], 100)
lb = Hero('刘备', '剑', ['头盔', '盔甲'], 100)
print(lb.blood)
hz.attack(lb)  # 黄忠攻击了刘备  
lb.run()  # 刘备逃跑了  

lb.attack(hz)  # 刘备杀回来了  
hz.run()  # 黄忠逃跑了  

print('刘备血量:', lb.blood)
print('黄忠血量:', hz.blood)