栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

10-Python面向对象编程(下)

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

10-Python面向对象编程(下)

  • ‍作者简介: 大家好,我是小火车~~
  • ️个人主页: 上进小火车的博客
  • 系列专栏: 本文收藏在《Python基础从0到1》专栏
  • ✉️如果文章知识点有错误的地方,请指正! 和大家一起学习一起进步
  • 如果觉得博主的文章还不错的话,请三连支持一下博主哦
  • ➡️各系列思维导图链接

Python面向对象编程
    • 一稜.继承
        • 1.1继承的概念
        • 1.2 实例----圆和矩形的面积
        • 1.3 改善代码
        • 1.4 注意点
    • 二索.改善实例2----英雄pk怪物
        • 2.1 用继承的知识改善代码
        • 2.2 进一步改善
    • 三燎. __new__()方法
        • 3.1 __new__()方法的使用
        • 3.2 注意点
    • 四.object---所有python类型的父类
    • 五.单例模式
    • 六葉.函数参数注解
    • 七 .扑克牌模拟
    • 八不.其他专栏


一稜.继承 1.1继承的概念

概念:子类自动拥有(继承)父类的所有方法和属性

继承的语法:

class 类名(父类名):
    pass
  • 子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发
  • 子类 中应该根据 职责,封装 子类特有的 属性和方法
  • 当 父类 的方法实现不能满足子类需求时,可以对方法进行 重写(override)
1.2 实例----圆和矩形的面积

举一个常见的例子。Circle 和 Rectangle ,不同的图形,面积(area)计算方式不同。

import math

class Circle:
    def __init__(self, color, r):
        self.r = r
        self.color = color

    def area(self):
        return math.pi * self.r * self.r

    def show_color(self):
        print(self.color)


class Rectangle:
    def __init__(self, color, a, b):
        self.color = color
        self.a, self.b = a, b

    def area(self):
        return self.a * self.b

    def show_color(self):
        print(self.color)

circle = Circle('red', 3.0)
print(circle.area())
circle.show_color()
rectangle = Rectangle('blue', 2.0, 3.0)
print(rectangle.area())
rectangle.show_color()

输出

28.274333882308138
red
6.0
blue
1.3 改善代码

我们看到Rectangle和Circle有同样的属性color和方法show_color 我们可以定义一个父类Shape,将Rectangle和Circle通用的部分提取到Shape类中,然后在子类的__init__方法中,通过 调用super().__init__(color) 把 color 传给父类的 __init__()

import math


class Shape:
    def __init__(self, color):
        self.color = color

    def area(self):
        return None

    def show_color(self):
        print(self.color)


class Circle(Shape):
    def __init__(self, color, r):
        super().__init__(color)
        # Shape.__init__(self,color) #这样也行,但是不好(考虑父类Shape的名字改变了,怎么办)
        self.r = r

    def area(self):
        return math.pi * self.r * self.r




class Rectangle(Shape):
    def __init__(self, color, a, b):
        super().__init__(color)
        # Shape.__init__(self, color)   #这样也行,但是不好(考虑父类Shape的名字改变了,怎么办)
        self.a, self.b = a, b

    def area(self):
        return self.a * self.b



circle = Circle('red', 3.0)
print(circle.area())
circle.show_color()
rectangle = Rectangle('blue', 2.0, 3.0)
print(rectangle.area())
rectangle.show_color()

输出

28.274333882308138
red
6.0
blue
1.4 注意点

子类Circle和Rectangle本身并没有定义show_color方法, 从父类Shape继承了show_color方法。子类Circle和Rectangle改写(Override)了父类的area方法,分别提供了自己不同的实现。

注释掉Circle类中__init__方法中super().__init__()这行代码:

class Circle(Shape):
    def __init__(self, color, r):
        # super().__init__(color)
        # Shape.__init__(self,color) #这样也行,但是不好(考虑父类Shape的名字改变了,怎么办)
        self.r = r

再次运行代码,报错:

  File "C:/Users/Administrator/PycharmProjects/untitled16/shape.py", line 12, in show_color
    print(self.color)
AttributeError: 'Circle' object has no attribute 'color'

错误输出指定的出错位置在:print(self.color),说Circle类型对象没有color属性

class Shape:
    def show_color(self):
        print(self.color)

问题原因是,Circle类型重写了父类Shape的__init__()方法,导致父类Shape的__init__()方法没有被调用到,所以

一定不要忘记在子类的__init__方法中调用super().__init__()

二索.改善实例2----英雄pk怪物 2.1 用继承的知识改善代码

我们发现Hero类和Monster类中有很多代码是重复的,我们把这些重复的代码提取到一个父类Sprite(精灵)中。

sprite.py

from random import randint


class Sprite:

    def __init__(self, flood,strength):
        self.flood = flood
        self.strength = strength

    def calc_health(self):
        return self.flood

    def take_damage(self, attack_sprite):
        damage = randint(attack_sprite.strength - 5, attack_sprite.strength + 5)
        self.flood -= damage
        print(f"{self.name}你被{attack_sprite.name}攻击,受到了{str(damage)}点伤害!还剩{str(self.flood)}滴血")
        if self.calc_health() <= 0:
            print(f"{self.name}你被杀死了!胜败乃兵家常事 请重新来过。")
            return True
        else:
            return False

hero.py

from sprite import Sprite


class Hero(Sprite):
    def __init__(self, name,flood,strength):
        self.name = name
        super().__init__(flood,strength)

monster.py

from sprite import Sprite


class Monster(Sprite):
    def __init__(self, name,flood,strength):
        self.name = name
        super().__init__(flood, strength)

pkgame.py不变

from hero import Hero
from monsters import Monster

hero = Hero('张三',100, 10)
monster = Monster('小强',60,20)

while True:
    is_monster_died = monster.take_damage(hero)
    if is_monster_died:
        break
    is_hero_died = hero.take_damage(monster)
    if is_hero_died:
        break

输出

小强你被张三攻击,受到了5点伤害!还剩55滴血
张三你被小强攻击,受到了19点伤害!还剩81滴血
小强你被张三攻击,受到了9点伤害!还剩46滴血
张三你被小强攻击,受到了25点伤害!还剩56滴血
小强你被张三攻击,受到了12点伤害!还剩34滴血
张三你被小强攻击,受到了21点伤害!还剩35滴血
小强你被张三攻击,受到了5点伤害!还剩29滴血
张三你被小强攻击,受到了23点伤害!还剩12滴血
小强你被张三攻击,受到了13点伤害!还剩16滴血
张三你被小强攻击,受到了16点伤害!还剩-4滴血
张三你被杀死了!胜败乃兵家常事 请重新来过。
2.2 进一步改善

假设我们想伤害值damage的大小,不在Sprite类里统一处理,而由各个具体的精灵(Hero或Monster)自己来决定,代码可以改造成如下: 在Sprite类中添加get_damage_value方法,不提供实现

from random import randint


class Sprite:

    def __init__(self, flood,strength):
        self.flood = flood
        self.strength = strength

    def calc_health(self):
        return self.flood

    def get_damage_value(self):
        pass

    def take_damage(self, attack_sprite):
        # damage = randint(attack_sprite.strength - 5, attack_sprite.strength + 5)
        damage = attack_sprite.get_damage_value();
        self.flood -= damage
        print(f"{self.name}你被{attack_sprite.name}攻击,受到了{str(damage)}点伤害!还剩{str(self.flood)}滴血")
        if self.calc_health() <= 0:
            print(f"{self.name}你被杀死了!胜败乃兵家常事 请重新来过。")
            return True
        else:
            return False

hero.py提供自己get_damage_value方法的实现

from random import randint

from sprite import Sprite


class Hero(Sprite):
    def __init__(self, name,flood,strength):
        self.name = name
        super().__init__(flood,strength)

    def get_damage_value(self):
        return randint(self.strength - 3, self.strength + 3)

monster.py提供自己get_damage_value方法的实现

from random import randint

from sprite import Sprite


class Monster(Sprite):
    def __init__(self, name,flood,strength):
        self.name = name
        super().__init__(flood, strength)

    def get_damage_value(self):
        return randint(self.strength - 7, self.strength + 7)

输出

小强你被张三攻击,受到了8点伤害!还剩52滴血
张三你被小强攻击,受到了13点伤害!还剩87滴血
小强你被张三攻击,受到了11点伤害!还剩41滴血
张三你被小强攻击,受到了19点伤害!还剩68滴血
小强你被张三攻击,受到了7点伤害!还剩34滴血
张三你被小强攻击,受到了22点伤害!还剩46滴血
小强你被张三攻击,受到了9点伤害!还剩25滴血
张三你被小强攻击,受到了25点伤害!还剩21滴血
小强你被张三攻击,受到了9点伤害!还剩16滴血
张三你被小强攻击,受到了24点伤害!还剩-3滴血
张三你被杀死了!胜败乃兵家常事 请重新来过。
三燎. new()方法 3.1 new()方法的使用

python中定义的类在创建实例对象的时候,会自动执行init()方法,但是在执行init()方法之前,会执行new()方法。

__new__()的作用主要有两个。

1.在内存中为对象分配空间

2.返回对象的引用。(即对象的内存地址)

python解释器在获得引用的时候会将其传递给__init__()方法中的self。

class A:
    def __new__(cls,*args,**kwargs):
        print('__new__')
        return super().__new__(cls)#这里一定要返回,否则__init__()方法不会被执行
    def __init__(self):#这里的self就是new方法中的return返回值
        print('__init__')

a = A()
-------------------------
#输出结果
__new__
__init__
3.2 注意点

我们一定要在__new__()方法中最后调用

return super().__new__(cls)

否则__init__()方法不会被调用

class A:
    def __new__(cls,*args,**kwargs):
        print('__new__')
        # return super().__new__(cls)#这里一定要返回,否则__init__()方法不会被执行
    def __init__(self):#这里的self就是new方法中的return返回值
        print('__init__')

a = A()
------------------------------------
__new__

像以前一样,我们不写__new__()方法试试

class A:
    # def __new__(cls,*args,**kwargs):
    #     print('__new__')
        # return super().__new__(cls)#这里一定要返回,否则__init__()方法不会被执行
    def __init__(self):#这里的self就是new方法中的return返回值
        print('__init__')


a = A()
---------------------------------------
__init__
四.object—所有python类型的父类

在Python中,所有类型有个隐式的父类(object),上面的代码相当于

class A(object):
    # def __new__(cls,*args,**kwargs):
    #     print('__new__')
        # return super().__new__(cls)#这里一定要返回,否则__init__()方法不会被执行
    def __init__(self):#这里的self就是new方法中的return返回值
        print('__init__')

a = A()

当我们的类中没有定义__new__()方法时,创建对象时,Python会先调用父类(object)的__new__()方法,在内存中创建一个实例,然后传递给我们的__init__()方法。如果我们的自定义类型定义了__new__()方法,就会覆盖父类(object)的__new__()方法(父类的__new__()方法不会被自动调用),所以我们要显式调用父类(object)的__new__()方法,在内存中创建一个实例。

那么__new__()方法有什么作用呢?我们可以改写它,举个例子,就比如说单例模式。

五.单例模式

如果我们创建两个实例:

a1 = A()
a2 = A()

那么id(a1)和id(a2)的值不一样,也就是说python在内容当中创建了两个实例对象,用了两份内存。同样的东西创建了两份

如果想不管创建多少个实例对象,我们都让它的id是一样的。

也就是说,先创建一个实例对象,之后不管创建多少个,返回的永远都是第一个实例对象的内存地址。可以这样实现:

# 重写new方法很固定,返回值必须是这个
# 这样就避免了创建多份。
# 创建第一个实例的时候,_instance是None,那么会分配空间创建实例。
# 此时的类属性已经被修改,_instance不再为None
# 那么当之后实例属性被创建的时候,由于_instance不为None。
# 则返回第一个实例对象的引用,即内存地址。
# 这样就应用了单例模式。
class A():
    _instance = None
    def __new__(cls,*args,**kwargs):
        if A._instance == None:
            A._instance = super().__new__(cls)
        return A._instance


a1 = A()
print(id(a1))
a2 = A()
print(id(a2))
六葉.函数参数注解

你写好了一个函数,然后想为这个函数的参数增加一些额外的信息(每个参数的类型),这样的话其他调用者就能清楚的知道这个函数应该怎么使用。

解决方案

使用函数参数注解是一个很好的办法,它能提示程序员应该怎样正确使用这个函数。

例如,下面有一个被注解了的函数:

def add(x:int, y:int) -> int:
    return x + y

python解释器不会对这些注解添加任何的语义。它们不会被类型检查,运行时跟没有加注解之前的效果也没有任何差距。 然而,对于那些阅读源码的人来讲就很有帮助啦。第三方工具和框架可能会对这些注解添加语义。同时它们也会出现在文档中。

>>> help(add)
Help on function add in module __main__:
add(x: int, y: int) -> int
>>>

尽管你可以使用任意类型的对象给函数添加注解(例如数字,字符串,对象实例等等),不过通常来讲使用类或者字符串会比较好点。

函数注解只存储在函数的 annotations 属性中。例如:

>>> add.__annotations__
{'y': , 'return': , 'x': }
>>>

尽管注解的使用方法可能有很多种,但是它们的主要用途还是文档。 因为python并没有类型声明,通常来讲仅仅通过阅读源码很难知道应该传递什么样的参数给这个函数。 这时候使用注解就能给程序员更多的提示,让他们可以正确的使用函数。

函数参数注解还给我们带来的好处有使得pycharm这样的IDE可以自动提示该类型对象拥有的方法。

class A:
    def dosomething(self):
        print('hello')


def foo(a : A):
    a.dosomething()

a = A()
foo(a)

在foo函数的定义中,如果我们没有指定a:A,当我们输入a.时,IDE提示不出来dosomething方法。

七 .扑克牌模拟

定义一个单张扑克类(考虑需要哪些属性),定义一个一副扑克牌类,该类包含一个单张扑克对象的数组(不考虑大小王)。实现一个模拟扑克发牌洗牌的算法; 2.电脑随机发出5张牌,判断是以下哪种牌型?(提示,利用各种集合的特性可以简化判断)

输出

参考代码

import random

colorLst = ['红桃', '方片', '黑桃', '梅花']


class Card:
    def __init__(self, color, value):
        self.value = value
        self.color = color

    def __str__(self):
        strvalue = str(self.value)
        if self.value == 11:
            strvalue = 'J'
        elif self.value == 12:
            strvalue = 'Q'
        elif self.value == 13:
            strvalue = 'K'
        elif self.value == 1:
            strvalue = 'A'

        return self.color + str(strvalue)


class Poke:
    def __init__(self):
        self.cards = []

        for color in colorLst:
            for value in range(1, 14):
                card = Card(color, value)
                self.cards.append(card)

    def output(self):
        index = 1
        for card in self.cards:
            print(card, end='t')

            if index % 13 == 0:
                print()

            index += 1


poke = Poke()
poke.output()

random.shuffle(poke.cards)
print('洗牌之后')
poke.output()

hands = poke.cards[:5]

print('分到的一手牌是')
for card in hands:
    print(card, end='t')

print('n牌型是:', end='')

bSameColor = False
bShunZi = False
cardColors = [card.color for card in hands]
cardValues = [card.value for card in hands]


cardValuesSet = set(cardValues)
cardColorsSet = set(cardColors)
if len(cardColorsSet) == 1:
    bSameColor = True

cardValuesSorted = sorted(cardValues)


if cardValuesSorted[4] - cardValuesSorted[0] == 4 and len(cardValuesSet) == 5:
    bShunZi = True

if bSameColor and bShunZi:
    print('同花顺')
    exit(0)
if bSameColor:
    print('同花')
    exit(0)
if bShunZi:
    print('顺子')
    exit(0)


if len(cardValuesSet) == 4:
    print('一对')
if len(cardValuesSet) == 5:
    print('杂牌')

if len(cardValuesSet) == 2:
    if cardValues.count(cardValues[0]) == 1 or cardValues.count(cardValues[0]) == 4:
        print('四带一')
    else:
        print('三带二')

bisThreeOneOne = False
if len(cardValuesSet) == 3:
    for value in cardValues:
        if cardValues.count(value) == 3:
            bisThreeOneOne = True
            break

    if bisThreeOneOne == False:
        print('221')
    else:
        print('311')

八不.其他专栏

❤️《Python基础从0到1》专栏
《Python进阶从0到1》专栏
《Python爬虫》专栏
《Python数据处理》专栏

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/882853.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号