python基础——面向对象

        最近看到一篇非常深奥的算法源码,发现它的底层逻辑全部都是由类实现的,而这种实现方式有非常大的好处,同样对于编程人员来讲,这种实现方式无疑增加了难度和挑战。没有受过系统训练的编程人员,常规的思路与编程习惯基本上都会是面向过程编程,很明显这种方式较为简单,但问题是这种思维方式,一旦遇到变换后其后期维护比较困难。好了转入正题,下面的内容也是自己在网上学习别人总结的资料,为方便后续学习与理解,全文内容属于转载【来源--知乎:纳西】,如对作者造成侵权,请及时联系我,我可及时删除,在此表示感谢。(大家可以跳转到原作者链接上进行系统学习)


1 面向对象(OOP)基本概念

1.1 过程和函数

  • 过程是早期的一个编程概念
  • 过程类似于函数,但只能执行,没有返回值
  • 函数不仅能执行,还可以返回结果

1.2 面向过程和面向对象

1.2.1 面向过程

  • 把某一个需求的所有步骤从头到尾逐步实现
  • 根据开发需求,将某些功能独立的代码封装成一个又一个函数
  • 最后顺序地调用不同的函数,完成代码

特点:

  • 注重步骤和过程,不注重职责分工
  • 开发复杂项目,没有固定的套路,开发难度很大

之前的名片管理系统:

python基础——面向对象_第1张图片

复杂的情况:一旦修改其中一个函数的参数,调用它的地方也要修改

python基础——面向对象_第2张图片

只封装功能在函数中
函数之间会彼此调用,关系复杂

1.2.2 面向对象——谁来做

  • 在完成某一个需求前,首先确定职责——要做的事情(方法)
  • 根据职责确定不同的对象,但对象内部封装不同的方法(多个)
  • 最后,顺序地让不同的对象调用不同的方法

特点:

  • 注重对象和职责
不仅将功能封装在方法中,还将方法封装在对象中。
不同对象不会调用对方的方法,关系简单

2 类和对象

2.1 概念

1、类

类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用

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

2、对象

对象是由类创建出来的一个具体存在,可以直接使用

由哪一个类创建出来的对象,就拥有哪一个类中定义的:属性、方法

在程序开发中,先有类,再有对象(类似于先有图纸,再有实物)

2.2 类和对象的关系

类只有一个,对象可以有多个

不同对象之间属性可能会各不相同
类中定义有什么属性和方法,对象也一样,不多不少

2.3 类的设计

在使用面向对象开发前,应该首先分析需求,确定程序中需要哪些类:

python基础——面向对象_第3张图片

设计一个类,通常要满足三个要素:

  • 类名:大驼峰命名法(例如GapWords),提炼需求中的名词
  • 属性:这个类的特征
  • 方法:这个类的行为

练习:

1、需求:

  • 小明今年18岁身高1.75,每天早上完步,会去东西
  • 小美今年17岁身高1.65,小美不跑步,小美喜欢东西

2、提炼:创建类
Person:name、age、height;run()、eat()

3 面向对象基本语法

3.1 dir()

变量、数据、函数都是对象

在python中可以用两个方法验证:

  1. ipython中,在标识符/数据后面输入.,然后按下tab键,可以查看对象内的属性和方法
  2. 使用内置函数dir传入标识符/数据,可以查看对象内所有属性及方法
def sum_number(num):
    # 递归的出口
    if num == 1:  # 累加的最小数字
        return 1
    # 【假设sum_number(num-1)能够得到(1+2+...+num-1)】
    temp = sum_number(num-1)
    return temp + num
​
​
print(dir(sum_number))

__方法名__格式的方法是python提供的内置方法/属性

序号 方法名 类型 作用
01 __new__ 方法 创建对象时,会被自动调用
02 __init__ 方法 对象被初始化时,会被自动调用
03 __del__ 方法 对象被从内存中销毁前,会被自动调用
04 __str__ 方法 返回对象的描述信息,print函数输出使用

利用好dir函数,很多方法不用死记硬背

3.2 定义简单的类(只包含方法)

3.2.1 定义类

class 类名:
    def 方法1(self, 参数列表):
        pass
    def 方法1(self, 参数列表):
        pass
方法定义的格式和之前类似,区别在于 第一个参数必须是self

3.2.2 创建对象

对象变量 = 类名()

3.2.3 练习——第一个面向对象程序

需求:小猫爱吃鱼,小猫要喝水

分析:Cat:eat()、 drink()

class Cat:
​
    def eat(self):
        print("小猫爱吃鱼")
​
    def drink(self):
        print("小猫要喝水")
​
​
# 创建对象
tom = Cat()
​
tom.eat()
tom.drink()
引用:在python中使用类创建对象之后,tom变量中 记录的是对象在内存中的地址
使用 print输出对象变量,默认情况下输出变量引用的对象 是由哪一个类创建的,以及 在内存中的地址(十六进制)
class Cat:
​
    def eat(self):
        print("小猫爱吃鱼")
​
    def drink(self):
        print("小猫要喝水")
​
​
# 创建对象
tom = Cat()
print(tom)  # <__main__.Cat object at 0x000001EAFAB264C0>
< main.Cat object at 0x000001EAFAB264C0>:
Cat类名,object对象,地址0x000001EAFAB264C0
意思:Cat对象在0x000001EAFAB264C0的地址
print(id(tom)) # 3049420584128
id()打印出来的地址是十进制的

%d %x:

%d:以十进制输出数字

%x:以十六进制输出数字

3.2.4 创建多个对象

class Cat:
​
    def eat(self):
        print("小猫爱吃鱼")
​
    def drink(self):
        print("小猫要喝水")
​
​
# 创建对象:不是同一只猫
tom = Cat()
lazy_cat = Cat()
print(tom) 
print(lazy_cat)
"""
<__main__.Cat object at 0x000002CD9CE874C0>
<__main__.Cat object at 0x000002CD9CE969A0>
"""
class Cat:
​
    def eat(self):
        print("小猫爱吃鱼")
​
    def drink(self):
        print("小猫要喝水")
​
​
# 创建对象:是同一只猫
lazy_cat = Cat()
lazy_cat2 = lazy_cat
print(lazy_cat)
print(lazy_cat2)
"""
<__main__.Cat object at 0x00000208E64E74C0>
<__main__.Cat object at 0x00000208E64E74C0>
"""

3.3 在类的外部给对象增加属性

class Cat:
​
    def eat(self):
        print("小猫爱吃鱼")
​
    def drink(self):
        print("小猫要喝水")
​
​
tom = Cat()
# 增加属性
tom.name = "Tom"
lazy_cat = Cat()

python基础——面向对象_第4张图片

3.4 self

class Cat:
​
    def eat(self):
        print("小猫爱吃鱼")
​
    def drink(self):
        print("小猫要喝水")
​
​
# 创建对象
tom = Cat()
tom.name = "Tom"
tom.drink()
tom.eat()
print(tom.name)

python基础——面向对象_第5张图片

self和tom的地址是一样的,且属性相同

哪一个对象调用的方法,self就是哪一个对象的引用

class Cat:
​
    def eat(self):
        print("%s 爱吃鱼" % self.name)
​
    def drink(self):
        print("小猫要喝水")
​
​
# 创建对象
tom = Cat()
tom.name = "Tom"
tom.drink()
tom.eat()  # Tom 爱吃鱼
print(tom.name)

在方法内部:

  • 可以通过self.访问对象的属性
  • 可以通过self.调用其他的对象方法

不建议在方法外部增加对象的属性:

# 会出错
class Cat:
​
    def eat(self):
        print("%s 爱吃鱼" % self.name)
​
​
# 创建对象
tom = Cat()
tom.eat()  
tom.name = "Tom"

3.5 初始化方法

3.5.1 概念

当使用类名()创建对象时,会自动执行以下操作:

  1. 为对象在内存分配空间——创建对象
  2. 为对象的属性设置初始值——初始化方法(init)
    这个初始化方法就是__init__方法
    __init__方法是专门用来定义一个类具有哪些属性的方法
# 验证创建对象时,会自动调用初始化方法
class Cat:
​
    def __init__(self):
        print("这是一个初始化方法")
​
​
# 创建对象
tom = Cat()
"""
这是一个初始化方法
"""

3.5.2 在初始化方法内部定义属性

定义属性,在__init__内部使用self.属性名 = 属性的初始值

定义属性之后,再使用类名()创建对象,该对象就拥有这个属性

class Cat:
​
    def __init__(self):
        print("这是一个初始化方法")
        self.name = "Tom"
​
​
# 创建对象
tom = Cat()
print(tom.name)
"""
这是一个初始化方法
Tom
"""

3.5.3 使用形参对初始化方法进行改造

之前的代码,name属性的值一直是"Tom"

要修改name,可以给__init__方法传入一个形参

class Cat:
​
    def __init__(self, new_name):
        print("这是一个初始化方法")
        self.name = new_name
​
​
# 创建对象
tom = Cat("Tom")
print(tom.name)
lazy_cat = Cat("大懒猫")
print(lazy_cat.name)
"""
这是一个初始化方法
Tom
这是一个初始化方法
大懒猫
"""

3.6 __del__方法

一个对象被从内存中销毁前,会自动调用__del__方法

应用场景:

如果希望在对象被销毁前,再做一些事情,可以考虑 __del__方法

生命周期:

一个对象调用 类名()创建,生命周期开始
一个对象的 __del__方法一旦被调用,生命周期结束
class Cat:
​
    def __init__(self, new_name):
        self.name = new_name
        print("%s 来了" % self.name)
​
    def __del__(self):
        print("%s 去了" % self.name)
​
​
# 创建对象
tom = Cat("Tom")
print(tom.name)
lazy_cat = Cat("大懒猫")
print(lazy_cat.name)
print("-" * 50)
"""
Tom 来了
Tom
大懒猫 来了
大懒猫
--------------------------------------------------
Tom 去了
大懒猫 去了
"""
class Cat:

    def __init__(self, new_name):
        self.name = new_name
        print("%s 来了" % self.name)

    def __del__(self):
        print("%s 去了" % self.name)


# 创建对象
tom = Cat("Tom")
print(tom.name)
del tom  # 删除对象
print("-" * 50)
"""
Tom 来了
Tom
Tom 去了
--------------------------------------------------
"""

3.7 __str__方法

使用print输出对象变量默认输出:变量引用的对象是由哪一个类创建的、对象在内存中的地址

如果希望使用print输出对象变量时,能够打印自定义内容,就可以利用__str__方法

注意: __str__方法 必须返回一个字符串
class Cat:

    def __init__(self, new_name):
        self.name = new_name
        print("%s 来了" % self.name)

    def __del__(self):
        print("%s 去了" % self.name)

    def __str__(self):
        # 必须返回一个字符串
        return "我是小猫[%s]" % self.name


# 创建对象
tom = Cat("Tom")
print(tom)
print("-" * 50)
"""
Tom 来了
我是小猫[Tom]
--------------------------------------------------
Tom 去了
"""

4 面对对象的三大特性之一——封装

4.1 封装

封装——将 属性方法 封装到一个抽象的

面向对象编程的第一步,将 属性方法封装到一个 抽象的类
外界使用类创建 对象,然后让对象 调用方法

4.2 案例

4.2.1 小明爱跑步

小明体重75.0公斤

小明每次跑步会减肥0.5公斤

小明每次吃东西体重增加1公斤

class Person:

    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __str__(self):
        return "%s的体重是%.2f 公斤" % (self.name, self.weight)

    def eat(self):
        print("吃东西")
        self.weight += 1.0

    def run(self):
        print("跑步")
        self.weight -= 0.5


xiaoMing = Person("小明", 76.0)
print(xiaoMing)
xiaoMing.eat()
print(xiaoMing)
xiaoMing.run()
print(xiaoMing)
"""
小明的体重是76.00 公斤
吃东西
小明的体重是77.00 公斤
跑步
小明的体重是76.50 公斤
"""

4.2.2 小明爱跑步扩展——小美也爱跑步

class Person:

    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __str__(self):
        return "%s的体重是%.2f 公斤" % (self.name, self.weight)

    def eat(self):
        print("吃东西")
        self.weight += 1.0

    def run(self):
        print("跑步")
        self.weight -= 0.5


xiaoMing = Person("小明", 76.0)
xiaoMei = Person("小美", 45.0)
print(xiaoMing)
print(xiaoMei)

4.2.3 摆放家具

  1. 房子户型总面积家具名称列表
  • 新房子没有任何的家具

2. 家具名字占地面积,其中

  • bed 4平米
  • chest 2平米
  • table 1.5平米

3. 将以上三件家具添加到房子中

4. 打印房子时,要求输出户型、总面积、剩余面积、家具名称列表

class HouseItem:

    def __init__(self, name, area):
        self.name = name
        self.area = area

    def __str__(self):
        return "[%s]占地 %.2f 平米" % (self.name, self.area)


class House:

    def __init__(self, house_type, area):
        self.house_type = house_type
        self.area = area
        # 剩余面积
        self.free_area = area
        # 家具名称列表
        self.item_list = []

    def __str__(self):
        # 【python能够将一对括号内的内容连接在一起,所以可以换行】
        return ("户型是[%s],\n总面积[%.2f]平米,\n剩余面积[%.2f]平米,\n家具有[%s]"
                % (self.house_type, self.area,
                   self.free_area, self.item_list))

    def add_item(self, item):
        print("要添加[%s]" % item) # 【输出的是HouseItem的__str__()方法定义的输出】
        if item.area > self.free_area:
            print("房子剩余面积不够,无法添加 %s " % item.name)
            return
        self.free_area = self.free_area - item.area
        self.item_list.append(item.name)


# 1、创建家具
bed = HouseItem("席梦思", 4)
chest = HouseItem("衣柜", 2)
table = HouseItem("餐桌", 1.5)

print(bed)
print(chest)
print(table)

# 2、创建房子对象
my_home = House("两室一厅", 70)

my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)

# 3、输出房子
print("-" * 50)
print(my_home)

4.3 一个对象的属性可以是另一个类创建的对象

案例:士兵突击

  • 士兵许三多有一把AK47
  • 士兵可以开火
  • 枪可以发射子弹
  • 枪装填子弹——增加子弹数量
# - 士兵许三多有一把AK47
# - 士兵可以开火

# - 枪可以发射子弹
# - 枪装填子弹——增加子弹数量
class Gun:
    def __init__(self, cartridge_name):
        self.cartridge_name = cartridge_name
        # 【子弹数量初始为0,不需要传递参数】
        self.cartridge_num = 0

    def __str__(self):
        return "%s 中还有 %d 颗子弹" % (self.cartridge_name, self.cartridge_num)

    def play(self):
        if self.cartridge_num == 0:
            print("子弹数量不够,无法开枪")
            return
        self.cartridge_num -= 1
        print("【发射子弹】... %d" % self.cartridge_num)

    def add(self, count):
        self.cartridge_num += count
        print("【装填子弹】... %d" % self.cartridge_num)


class Soldier:
    def __init__(self, name):
        self.name = name
        # 新兵没有枪【可以用None赋值给属性】
        self.gun = None

    def __str__(self):
        if self.gun is None:
            return "士兵 [%s] 没有枪" % self.name
        else:
            return "士兵 [%s] 有一把 [%s]" % (self.name, self.gun.cartridge_name)

    def shoot(self):
        # 1、判断士兵有没有枪
        if self.gun is None:
            print("士兵[%s]没有枪..." % self.name)
            return
        # 2、高喊口号
        print("【士兵开枪】")
        # 3、让枪装填子弹
        self.gun.add(50)
        self.gun.play()


AK47 = Gun("AK47")
print(AK47)
xuSanDuo = Soldier("许三多")
print(xuSanDuo)
xuSanDuo.gun = AK47
print(xuSanDuo)
xuSanDuo.shoot()
xuSanDuo.shoot()

4.4 身份运算符 is、is not

用于比较两个对象的内存地址是否一致(是否是对同一个对象的引用)

运算符 描述 实例
is is是判断两个标识符是否引用同一个对象 x is y类似id(x)==id(y)
is not 判断两个标识符引用不同对象 x is not y类似id(x)!=id(y)

is和==的区别:

==判断变量的值

is判断变量的地址

a = [1, 2, 3]
b = [1, 2]
b.append(3)
print("a的地址%d" % id(a))
print("b的地址%d" % id(b))
print(a == b)  # True
print(a is b)  # False

None:

对于None的比较,建议使用is判断

4.5 私有属性和私有方法

4.5.1 应用场景

对象的某些属性或方法只希望在对象的内部被使用,而不希望在外部被访问到

4.5.2 定义方法

在属性名或者方法名前增加两个下划线

class Women:

    def __init__(self, name):
        self.name = name
        self.__age = 18

    def secret(self):
        print("%s 的年龄是 %d" % (self.name, self.__age))


xiaofang = Women("小芳")
# print(xiaofang.__age)  # 报错:在外界私有属性不能被访问
xiaofang.secret()  # 可以执行:私有属性在方法内部可以被访问

class Women:

    def __init__(self, name):
        self.name = name
        self.__age = 18

    def __secret(self):
        print("%s 的年龄是 %d" % (self.name, self.__age))


xiaofang = Women("小芳")
# print(xiaofang.__age)  # 报错:在外界私有属性不能被访问
xiaofang.__secret()  # 报错

4.5.3 python中没有真正意义的私有

在给属性、方法命名时,实际是对名称做了一些特殊处理,使得外界无法访问到

访问方法:在名称前面加上 _类名
class Women:

    def __init__(self, name):
        self.name = name
        self.__age = 18

    def __secret(self):
        print("%s 的年龄是 %d" % (self.name, self.__age))


xiaofang = Women("小芳")
print(xiaofang._Women__age)  # 18
xiaofang._Women__secret()  # 小芳 的年龄是 18

在开发中,不要使用这种方法去访问对象的私有属性或私有方法(既然设置为隐私,就不要访问)

5 面向对象的三大特性之一——继承

5.1 概念

继承:实现代码的重用

子类继承自父类,可以使用父类所有的属性和方法
子类中应该根据职责,封装子类特有的属性和方法

5.2 语法

class 类名(父类名):

pass

class Animal:

    def eat(self):
        print("吃")

    def drink(self):
        print("喝")

    def run(self):
        print("跑")

    def sleep(self):
        print("睡")


# 子类拥有父类所有的属性和方法
class Dog(Animal):
    def bark(self):
        print("汪汪叫")


wangcai = Dog()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()

5.3 专业术语

Dog类是Animal类的子类Animal类是Dog类的父类Dog类从Animal继承

Dog类是Animal类的派生类Animal类是Dog类的基类Dog类从Animal派生

5.4 继承的传递性

C类从B类继承,B类从A类继承。那么C类具有B类和A类的所有属性和方法

class Animal:

    def eat(self):
        print("吃")

    def drink(self):
        print("喝")

    def run(self):
        print("跑")

    def sleep(self):
        print("睡")


# 子类拥有父类所有的属性和方法
class Dog(Animal):
    def bark(self):
        print("汪汪叫")


# 继承具有传递性
class XiaoTian(Dog):
    def fly(self):
        print("哮天犬会飞")


wangcai = XiaoTian()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
wangcai.fly()

python基础——面向对象_第6张图片

可以看出Animal类的被Dog和XiaoTian继承

5.5 注意事项

class Animal:

    def eat(self):
        print("吃")

    def drink(self):
        print("喝")

    def run(self):
        print("跑")

    def sleep(self):
        print("睡")


# 子类拥有父类所有的属性和方法
class Dog(Animal):
    def bark(self):
        print("汪汪叫")


class Cat(Animal):
    def bark(self):
        print("喵喵叫")


class XiaoTian(Dog):
    def fly(self):
        print("哮天犬会飞")


wangcai = XiaoTian()
wangcai.bark()  # 汪汪叫
# XiaoTian类不能使用Cat类的方法和属性,他们没有继承关系
XiaoTian类不能使用Cat类的方法和属性,他们没有继承关系

5.6 方法的重写

5.6.1 覆盖父类的方法

当父类中的方法不能满足子类需求时,可以对方法进行重写

实现方法:在子类中定义一个同名方法

class Dog(Animal):
    def bark(self):
        print("汪汪叫")


class XiaoTian(Dog):
    # 方法重写
    def bark(self):
        print("嗷嗷嗷~")

    def fly(self):
        print("哮天犬会飞")


wangcai = XiaoTian()
wangcai.bark() # 嗷嗷嗷~

5.6.2 对父类方法进行扩展

方法:

  1. 在子类重写父类方法
  2. 在需要的位置使用super().父类方法来调用父类方法
class Dog(Animal):
    def bark(self):
        print("汪汪叫")


class XiaoTian(Dog):
    # 方法重写
    def bark(self):
        # 1.针对子类特有的需求,编写代码
        print("嗷嗷嗷~")
        # 2.使用super().调用父类中的方法
        super().bark()
        # 3.增加其他代码
        print("吼吼吼~")

    def fly(self):
        print("哮天犬会飞")


wangcai = XiaoTian()
wangcai.bark()

"""
嗷嗷嗷~
汪汪叫
吼吼吼~
"""

super:

  1. super是一个特殊的类
  2. super()是使用super类创建出来的对象
  3. 最常使用的场景是重写父类方法时,调用在父类中封装的方法

调用父类方法的另一种方式:

python2.x时,使用方法:

父类名.方法(self)

python3.x不推荐使用(能用),因为一旦父类发生变化,调用位置的父类名同样需要修改

class Dog(Animal):
    def bark(self):
        print("汪汪叫")


class XiaoTian(Dog):
    # 方法重写
    def bark(self):
        # 1.针对子类特有的需求,编写代码
        print("嗷嗷嗷~")
        # 2.使用super().调用父类中的方法
        Dog.bark(self)
        # 3.增加其他代码
        print("吼吼吼~")

    def fly(self):
        print("哮天犬会飞")


wangcai = XiaoTian()
wangcai.bark()

"""
嗷嗷嗷~
汪汪叫
吼吼吼~
"""

5.7 父类的私有属性和私有方法

  1. 子类对象 不能在自己的方法内部,直接访问父类的私有属性或私有方法
  2. 子类对象 可以通过父类的公有方法 间接访问到私有属性或私有方法
class A:
    def __init__(self):
        self.__name = "A"

    def __test1(self):
        print("父类的私有方法")

    def test(self):
        print("父类的公有方法:")
        print("父类中的私有属性 %s" % self.__name)
        self.__test1()


class B(A):
    pass


b = B()
b.test()
"""
父类的公有方法:
父类中的私有属性 A
父类的私有方法
"""

5.8 多继承

5.8.1 语法

class 子类名(父类名1, 父类名2...)

pass

class A:

    def test(self):
        print("test方法")


class B:

    def demo(self):
        print("demo方法")


class C(A, B):
    pass


c = C()
c.test()
c.demo()

5.8.2 注意事项

不同的父类中有同名的方法,应该避免使用多继承

class A:

    def test(self):
        print("A---test方法")

    def demo(self):
        print("A---demo方法")


class B:

    def test(self):
        print("B---test方法")

    def demo(self):
        print("B---demo方法")


class C(A, B):
    pass


c = C()
c.test()
c.demo()
"""
A---test方法
A---demo方法
"""

5.8.3 MRO——方法搜索顺序

python针对类提供了一个内置属性__mro__,可以查看方法搜索顺序

主要用于多继承时判断方法、属性的调用路径

语法:

对象.__mro__
class A:

    def test(self):
        print("A---test方法")

    def demo(self):
        print("A---demo方法")


class B:

    def test(self):
        print("B---test方法")

    def demo(self):
        print("B---demo方法")


class C(A, B):
    pass


c = C()

# 确定C类对象调用方法的顺序
print(C.__mro__)
"""
(, , , )
顺序:C-A-B-object 【object是所有类的基类】
"""

5.8.4 新式类、旧式(经典)类

object是python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看

新式类:以object为基类的类

经典类:不以object为基类的类,不推荐使用

python3.x中,默认 object是基类
python2.x中,如果没有指定父类,则不会以object作为基类

为了保证代码在python2.xpython3.x中都可以运行,在定义类时,如果没有父类,建议统一继承自object

class 类名(object):
	pass

6 面向对象的三大特性之一——多态

多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

前提:继承、重写父类方法

class Dog(object):

    def __init__(self, name):
        self.name = name

    def game(self):
        print("%s 蹦蹦跳跳地玩耍" % self.name)


class XiaoTianDog(Dog):

    def game(self):
        print("%s 飞到天上玩耍" % self.name)


class Person(object):

    def __init__(self, name):
        self.name = name

    def game_with_dog(self, dog):
        print("%s 和 %s 快乐地玩耍" % (self.name, dog.name))
        dog.game()


wangCai = Dog("旺财")
xiaoMing = Person("小明")
xiaoMing.game_with_dog(wangCai)
"""
小明 和 旺财 快乐地玩耍
旺财 蹦蹦跳跳地玩耍
"""
wangCai = XiaoTianDog("飞天旺财")
xiaoMing = Person("小明")
xiaoMing.game_with_dog(wangCai)
"""
小明 和 飞天旺财 快乐地玩耍
飞天旺财 飞到天上玩耍
"""

7 类属性

7.1 术语

  1. 使用面向对象开发,第一步是设计
  2. 使用类名()创建对象,创建对象有两步:
    1)在内存为对象分配空间
    2)调用初始化方法__init__为对象初始化
  3. 对象创建后,内存中就有了一个对象的存在——实例

python基础——面向对象_第7张图片

创建了三个对象,它们的内存位置不同,保存各自不同的属性
对象的方法只保存在一个位置,不同对象使用方法时,通过传递参数调用

因此:

  1. 创建出来的对象叫做类的实例
  2. 创建对象的动作叫做实例化
  3. 对象的属性叫做实例属性
  4. 对象调用的方法做实例方法

7.2 类是一个特殊的对象

python中一切皆对象,

class AAA:定义的类属于 类对象
obj1 = AAA()属于 实例对象

类对象可以拥有自己的属性和方法:

  1. 类属性
  2. 类方法
通过 类名.的方式可以访问类的属性或者调用类的方法

python基础——面向对象_第8张图片

7.3 类属性和实例属性

  1. 类属性就是给类对象中定义的属性
  2. 通常用来记录与这个类相关的特征
  3. 类属性不会用于记录具体对象的特征

语法:

使用赋值语句定义

使用类.类属性调用

示例:

  • 定义一个工具类
  • 每件工具都有自己的name
  • 需求——知道使用这个类,创建了多少个工具对象
class Tool(object):

    # 定义类属性
    count = 0

    def __init__(self, name):
        self.name = name

        # 让类属性的值+1
        Tool.count += 1

tool1 = Tool("斧头")
print(Tool.count)
tool2 = Tool("榔头")
print(Tool.count)
tool3 = Tool("水桶")
print(Tool.count)

属性的获取——向上查找机制

python属性的获取存在一个向上查找机制

python基础——面向对象_第9张图片

因此,要访问类属性有两种方式:

  1. 类名.类属性
  2. 对象.类属性(不推荐:读取时没有问题;设置值时不可以)
class Tool(object):

    # 定义类属性
    count = 0

    def __init__(self, name):
        self.name = name

        # 让类属性的值+1
        Tool.count += 1

tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")
# 方式一
print(Tool.count)
# 方式二
print(tool1.count)
print(tool3.count)

注意:

  • 使用对象.类属性 = 值,只会给对象添加属性,不会影响到类属性的值
  • tool3.count = 100
    tool3内存空间查找是否有count属性,没有就添加;有就修改值
class Tool(object):

    # 定义类属性
    count = 0

    def __init__(self, name):
        self.name = name

        # 让类属性的值+1
        Tool.count += 1

tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")
tool1.count = 100
# 方式一
print(Tool.count)  # 3
# 方式二
print(tool1.count)  # 100
print(tool3.count)  # 3

8 类方法

访问到类属性

8.1 语法

@classmethod
def 类方法名(cls):
    pass
需要用 修饰器 @classmethod来标识, 告诉解释器这是一个类方法
类方法的 第一个参数应该是 cls
  • 由哪一个类调用的方法,cls就是哪一个类的引用
  • 和实例方法中的self类似
  • 提示:使用其他名称也可,不过习惯用cls

8.2 案例

  • 定义一个工具类
  • 每件工具都有自己的name
  • 需求——在类封装一个show_tool_count的类方法,输出使用当前这个类创建的对象个数
class Tool(object):

    # 定义类属性
    count = 0

    @classmethod
    def show_tool_count(cls):
        print("工具对象的个数 %d" % cls.count)  # 使用【cls.count】

    def __init__(self, name):
        self.name = name

        # 让类属性的值+1
        Tool.count += 1


tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")

Tool.show_tool_count()

9 静态方法

9.1 应用场景

  • 既不需要访问实例属性或者调用实例方法
  • 也不需要访问类属性或者调用类方法

9.2 语法

@staticmethod
def 静态方法名():  # 不需要self
    pass
需要修饰器 @staticmethod告诉解释器,这是一个静态方法
通过 类名. 调用静态方法
class Dog(object):

    @staticmethod
    def run():
        print("小狗要跑")


# 不创建对象,直接调用静态方法
Dog.run()

9.3 案例

  1. 设计一个Game类
  2. 属性:
  • 定义一个类属性top_score记录游戏的历史最高分
  • 定义一个实例属性player_name记录当前游戏的玩家姓名

3. 方法:

    • 静态方法show_help显示游戏帮助信息
    • 类方法show_top_score显示历史最高分
    • 实例方法start_game开始当前玩家的游戏

4. 主程序步骤

    • 查看帮助信息
    • 查看历史最高分
    • 创建游戏对象,开始游戏
class Game(object):

    # 历史最高分
    top_score = 0

    def __init__(self, name):
        # 玩家姓名
        self.player_name = name

    @staticmethod
    def show_help():
        print("游戏帮助信息...")

    @classmethod
    def show_top_score(cls):
        print("游戏最高分为 %d" % cls.top_score)

    def start_game(self):
        print("【%s】开始游戏" % self.player_name)


# - 查看帮助信息
Game.show_help()
# - 查看历史最高分
Game.show_top_score()
# - 创建游戏对象,开始游戏
xiaoMing = Game("小明")
xiaoMing.start_game()

小结

  1. 实例方法——方法内部需要访问 实例属性
  • 实例方法内部可以使用 类名. 访问类属性

2. 类方法——方法内部需要访问 类属性

3. 静态方法——方法内部不需要访问类属性和实例属性

你可能感兴趣的:(python基础,python,面向对象)