第三周 周末总结

周末总结

day01

  • 玩家摇两颗色子,如果第一次摇出了7点或11点,玩家胜;如果摇出了2点、3点、12点,庄家胜;
    如果摇出了其他的点数,游戏继续,玩家重新摇色子;如果玩家摇出了第一次摇的点数,玩家胜;
    如果玩家摇出了7点,庄家胜;如果玩家摇出其他点数,游戏继续,玩家重新摇色子,直到分出胜负。

    游戏开始之前,玩家有1000元的初始资金,玩家可以下注,赢了获得下注的金额,输了就扣除下注的金额,
    游戏结束的条件是玩家把钱输光。

import random
money = 1000
while money > 0:
    print(f'玩家的总资产{money}')
    duzhu = 0  
   # 这里赌注不能写在外面,不然循环会一直执行下去,不会重新下注
    while duzhu <= 0 or duzhu > money:
        try:
            duzhu = int(input('请下注:'))
        except ValueError:
            print('请重新下注:')
    ## 前面的注码是0,所以会执行while循环,正确不循环,错误会进入循环
    ## except 可以避免错误输出在屏幕上
    first_point = random.randrange(1, 7) + random.randrange(1, 7)
    # print(f'摇出的色子{first_point}')
    if first_point in (7, 11):
        print('玩家胜')
        money += duzhu
    elif first_point in (2, 3, 12):
        print('庄家胜')
        money -= duzhu
    else:
        while True:
            curr_point = random.randrange(1, 7) + random.randrange(1, 7)
            # print(f'摇出的色子{curr_point}')
            if curr_point == first_point:
                print('玩家胜')
                money += duzhu
                break  #代表这轮游戏结束,重新开始
            elif curr_point == 7:
                print('庄家胜')
                money -= duzhu
                break
print('游戏结束,玩家的钱已被坑完')
  • 红色球01-33,选择不重复的6个球,按从小到大排列
    蓝色球01-16,选择一个球,跟在红色球的后面
import random

red_balls = [i for i in range(1, 34)]
blue_balls = [i for i in range(1, 16)]
select_balls = random.sample(red_balls,6)
# sample 生成列表
select_balls.sort()
blue_ball = random.sample(blue_balls,1)
# select_balls.append(blue_ball)
# append()是以元素什么新式增加就以什么形式添加列表 

# select_balls.extend(blue_ball)
# 是把元素去掉列表形式添加到列表

select_balls += random.choices(blue_balls,k = 1)


for ball in select_balls:
    print(f'{ball:0>2d}',end=' ')

注意的知识点:列表的合并

  1. append()是以元素什么新式增加就以什么形式添加列表。
  2. extend()是把元素去掉列表形式添加到列表。
  3. list1 + = list2是把列表2元素添加到列表1中,并不会以列表的形加入。
  4. sort()不能赋值,但sorted()函数可以进行赋值操作。
  5. randon.sample(对象,取值数),可以进行赋值操作,与其一样还有choices(生成列表)和choice(不生成列表)
函数
函数的作用

不知大家是否注意到,上面的代码中我们做了三次求阶乘,虽然mnm - n的值各不相同,但是三段代码并没有实质性的区别,属于重复代码。世界级的编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”。要写出高质量的代码首先要解决的就是重复代码的问题。对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称为“函数”的代码块中,在需要计算阶乘的地方,我们只需要“调用函数”就可以了。

定义函数

数学上的函数通常形如y = f(x)或者z = g(x, y)这样的形式,在y = f(x)中,f是函数的名字,x是函数的自变量,y是函数的因变量;而在z = g(x, y)中,g是函数名,xy是函数的自变量,z是函数的因变量。Python中的函数跟这个结构是一致的,每个函数都有自己的名字、自变量和因变量。我们通常把Python中函数的自变量称为函数的参数,而因变量称为函数的返回值。

自变量(argument) ——》 参数(parameter)

因变量 ——》 返回值(return value)

在Python中可以使用def关键字来定义函数,和变量一样每个函数也应该有一个漂亮的名字,命名规则跟变量的命名规则是一致的(赶紧想一想我们之前讲过的变量的命名规则)。在函数名后面的圆括号中可以放置传递给函数的参数,就是我们刚才说到的函数的自变量,而函数执行完成后我们会通过return关键字来返回函数的执行结果,就是我们刚才说的函数的因变量。一个函数要执行的代码块(要做的事情)也是通过缩进的方式来表示的,跟之前分支和循环结构的代码块是一样的。大家不要忘了def那一行的最后面还有一个:,那是在英文输入法状态下输入的冒号。

我们可以通过函数对上面的代码进行重构。所谓重构,是在不影响代码执行结果的前提下对代码的结构进行调整。

m = int(input('请输入一个整数'))
n = int(input('请输入一个整数'))

def fac(num):
    """
    求数的阶乘
    :param num:
    :return:
    """
    result = 1
    for i in range(2, num + 1):
        result *= i
    return result

print(fac(m)//fac(n)//fac(m - n))
全局变量和局部变量
  • 全局变量(没有写在任何函数里面的变量):起作用的是全局。
  • 局部变量(定义在函数内部的变量):起作用的部分仅仅是定义函数内。

两个变量相互没有影响,各自不影响。

Python程序中搜索一个变量是按照 LEGB 顺序进行搜索的

Local(局部作用域) —> Embeded(嵌套作用域) —> Global(全局作用域)
—> Built-in(内置作用域) —> NameError: name … not defined

global —> 声明使用全局变量或者定义一个局部变量将其放到全局作用域
nonlocal —> 声明使用嵌套作用域的变量(不使用局部变量)

x = 100
#  全局变量

def foo():
    # 如果我不想在函数foo中定义局部变量x,想直接使用全局变量x,应该怎么做???
    # global x 这个x是来自于全局 把全局变量变成200
    # 所以加上global输出 300(嵌套的的300) 200 200
    x = 200

    def bar():
        # 如果我不想在函数bar中定义局部变量x,想直接使用嵌套作用域中的x,应该怎么做???
        # nonlocal x 
        # 不能与global一起用
        # 加上后,外面局部变量的x不会用,用嵌套的嵌套里的x
        # 300 300 100
        x = 300
        print(x)

    bar()
    print(x)


foo()
print(x)

# 没有g...和n...时:输出 300 200 100
递归函数

feibolaqi数列
1, 1, 2, 3, 5, 8, 13,21, 34, 55, 。。。。
f(n) =f(n-1) + f(n -2)

  1. 递归函数的两个要点:
    1.递归公式(第n次跟第n-1次的关系)
    2.收敛条件(什么时候停止递归的调用)

    3.自己调用自己

# 省时间和省空间不可以二者兼得
def fib(n, temp={}):
    if n in (1, 2):
        return 1
    if n not in temp:
        temp[n] = fib(n - 1) + fib(n - 2)
        # 该if是减少递归循环的时间
    return temp[n]

if __name__ == '__main__':
    for i in range(1,21):
        print(i,fib(i))
注释

在自己写的函数中需要加上文档注释:

def ...():
	"""
	文档注释
	"""

原因:

  • 因为自己写的函数不一定是自己调用,可能其他人也会调用这个函数,如果没有文档注释,别人就无法理解这个函数在做什么,也不知道如何使用这个函数。
  • 加了之后,调用函数时,可以看到函数的文档注释(使用说
PC中物理重新构造函数的方法

选中需要构建的函数代码,右键选择Refactor,在选择Extract Method…

提示框出来后,加上在第一栏写上函数的名称就点🆗。

day02

一、写一个实现生成指定长度的随机验证码(有数字和英文字母构成)的函数

Snake case —> Python标识符命名风格

import random
import string


def get_captcha_code(length: int = 4) -> str:
    """生成随机验证码

    :param length: 验证码的长度
    :return: 随机验证码字符串
    """
    selected_chars = random.choices(string.digits + string.ascii_letters, k=length)
    return ''.join(selected_chars)


for _ in range(10):
    print(get_captcha_code())
二、写一个函数判断一个正整数是不是质数。
def is_prime(num: int) -> bool:
    """判断一个正整数是不是质数

     :param num: 正整数
     :return: 如果是质数返回True,否则返回False
     """
     for i in range(2, int(num ** 0.5) + 1):
         if num % i == 0:
             return False
     return num != 1

for n in range(2, 100):
    if is_prime(n):
        print(n, end=' ')
三 、用函数实现求两个数的最大公约数和最小公倍数。

写程序的终极原则:高内聚,低耦合 —> high cohesion low coupling

设计函数最为重要的原则:单一职责原则(一个函数只做好一件事情)—> 高度内聚

GCD —> 欧几里得算法(辗转求余数法)

def gcd(x: int, y: int) -> int:
    """求最大公约数"""
    while y % x != 0:
        x, y = y % x, x
    return x


def lcm(x: int, y: int) -> int:
    """求最小公倍数"""
    return x * y // gcd(x, y)


print(gcd(27, 15))
print(lcm(27, 15))
四、设计获得样本数据描述性统计信息的函数

集中趋势:均值、中位数、众数
离散趋势:极差、方差、标准差


import math
import random


def ptp(data):
    """求极差(全距)"""
    return max(data) - min(data)


def average(data):
    """求均值"""
    return sum(data) / len(data)


def variance(data):
    """求方差"""
    x_bar = average(data)
    temp = [(num - x_bar) ** 2 for num in data]
    return sum(temp) / (len(temp) - 1)


def standard_deviation(data):
    """求标准差"""
    return math.sqrt(variance(data))


def median(data):
    """找中位数"""
    temp, size = sorted(data), len(data)
    if size % 2 != 0:
        return temp[size // 2]
    else:
        return average(temp[size // 2 - 1: size // 2 + 1])


# __name__是一个隐藏变量,它代表了当前模块(文件)的名字
# 如果直接通过python解释器运行importhomework04.py这个文件,这个 __name__的名字是__main__’
# 当其他文件模块导入importhomework04时,就不会时‘__main__’这个名字,是importhomework04
if __name__ == '__main__':
    nums = [random.randrange(1, 100) for _ in range(8)]
    print(nums)
    print(f'均值: {average(nums)}')
    print(f'中位数: {median(nums)}')
    print(f'极差: {ptp(nums)}')
    print(f'方差: {variance(nums)}')
    print(f'标准差: {standard_deviation(nums)}')

day03

一、关键字

在设计函数的时候,函数的参数个数是暂时无法确定
argument
位置参数:
关键字参数:
关键字参数一定在位子参数的后面

’*‘前面时位置参数,后面是关键字参数,必须’=‘赋值。

参数可以给出默认值,当调用函数未给出默认值时,不会替换默认值,而会调用定义时的默认值

def calc(*args, **kwargs):
    total = 0
    result = 0
    for arg in args:
        if type(arg) in (int,float):
            total += arg
    for value in kwargs.values():
        if type(value) in (int,float):
            result += value
    return total,result


print(calc())                  # 0
print(calc(1, 2, 3))           # 6
print(calc(a=1, b=2, c=3))     # 6
print(calc(1, 2, c=3, d=4))    # 10
二、一阶函数和高阶函数

python中的函数的一等函数:
1.函数可以作为函数的参数
2函数可以作为函数的返回值,
3.函数可以赋值给变量

如果把函数作为函数的参数或返回值,这种玩法通常称之为高阶函数
通常使用高阶函数

def calc(*args, init_value, op, **kwargs):
    total = init_value
    for arg in args:
        if type(arg) in (int, float):
            total = op(total, arg)
    for value in kwargs.values():
        if type(value) in (int, float):
            total = op(total, value)
    return total


def add(x, y):
    return x + y


def mul(x, y):
    return x * y


def sub(x, y):
    return x - y

print(calc( 1,  2,  3,init_value=0,op=operator.add,  x=4, y=5))      # 15
print(calc(1, 2,  y=4, z=5,init_value=1, op=mul,))

设计函数的时候,一定要注意函数的无副作用性(调用函数不能影响传入无副作用性),不会改变调用者

lambda(匿名函数)

在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,那么我们可以使用Lambda函数来表示。Python中的Lambda函数是没有的名字函数,所以很多人也把它叫做匿名函数,匿名函数只能有一行代码,代码中的表达式产生的运算结果就是这个匿名函数的返回值。

import operator, functools

#一行代码定义求阶乘的函数

fac = lambda num: functools.reduce(operator.mul, range(1, num + 1), 1)

#一行代码定义判断素数的函数

is_prime = lambda x: x > 1 and all(map(lambda f: x % f, range(2, int(x ** 0.5) + 1)))

#调用Lambda函数

print(fac(10))        # 3628800
print(is_prime(9))    # False

第二个代码的op=()和lambda函数一起用。

day04

一.面向对象编程

指令式编程 ——> 面向过程(函数)编程 ——> 程序比较简单的时候没有任何毛病

编程范式(程序设计方法论):面向对象编程 / 函数式编程

对象:对象是可以接收消息的实体,面向对象编程就是通过给对象发消息达到解决问题的目标

对象 = 数据 + 函数(方法) —–> 对象将数据和操作数据的函数从逻辑上变成一个整体

一起皆为对象

对象都有属性和行为

每个对象都是独一无二

对象一定属于某个类

类:将有共同特征(静态特征和动态特征)的对象的共同特征抽取出来之后得到的一个抽象概念。

简单的说,类是对象的蓝图(模板),有了类才能够创建出这种类型的对象。

面向对象编程:

  1. 定义类 —> 类的命名使用驼峰命名法(每个单词首字母大写)
    数据抽象:找到和对象相关的静态特征(属性)—> 找名词
    行为抽象:找到和对象相关的动态特征(方法)—> 找动词
  2. 造对象
  3. 发消息
# 第一步:定义类
class Student:
    """学生"""

    # 数据抽象(属性)
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 行为抽象(方法)
    def eat(self):
        """吃饭"""
        print(f'{self.name}正在吃饭.')

    def study(self, course_name):
        """学习"""
        print(f'{self.name}正在学习{course_name}.')

    def play(self, game_name):
        """玩"""
        print(f'{self.name}正在玩{game_name}.')

            
            
from example01 import Student

# 第二步:创建对象 ---> 构造器语法 ---> 类名(..., ...)
stu1 = Student('王大锤', 15)
stu2 = Student('骆昊', 41)

# Student.study(stu1, 'Python程序设计')
# 第三步:给对象发消息(调用对象的方法)
stu1.study('Python程序设计')
stu1.eat()

stu2.play('斗地主')

stu1.age = 20
二.用面向对象编程思想解决现实问题

给定一个圆环水池,围墙每米造价38.5元,过道宽3米,每米造价58.5元。

init —> initialize —> 初始化

1.抽象:找到问题的本质,核心对象:大圆,小圆(定义圆类,创建两个圆的对象,通过给对象发消息求解问题)

import math


class Circle:

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

    def perimeter(self):
        return 2 * math.pi * self.radius

    def area(self):
        return math.pi * self.radius ** 2


if __name__ == '__main__':
    r = float(input('请输入游泳池的半径: '))
    c1, c2 = Circle(r), Circle(r + 3)

    fence_price = c2.perimeter() * 38.5
    aisle_price = (c2.area() - c1.area()) * 58.5

    print(f'围墙的造价: {fence_price:.2f}元')
    print(f'过道的造价: {aisle_price:.2f}元')
    print(f'总计: {fence_price + aisle_price: .2f}元')

创建一个时钟对象(可以显示时/分/秒),让它运转起来(走字)。

名词:
时钟 —> 类
时、分、秒 —> 属性
动词: 显示、走字
class Clock:
    """时钟"""

    def __init__(self, hour=0, minute=0, second=0):
        """数据抽象:属性"""
        self.hour = hour
        self.minute = minute
        self.second = second

    def show(self):
        """显示时间"""
        return f'{self.hour:0>2d}:{self.minute:0>2d}:{self.second:0>2d}'

    def run(self):
        """走字"""
        self.second += 1
        if self.second == 60:
            self.second = 0
            self.minute += 1
            if self.minute == 60:
                self.minute = 0
                self.hour += 1
                if self.hour == 24:
                    self.hour = 0


if __name__ == '__main__':
    clock = Clock(23, 59, 58)
    while True:
        os.system('cls')
        print(clock.show())
        time.sleep(1)
        clock.run()

注意知识点:

  • os:需要import导入,可以在Terminal显示,苹果端的命令时os.system('clear'),在安卓端时cls
  • 0>2d:输出格式化,占两位,没有数值以0来填补。
  • 在定义属性是可以定义默认值。
三.定义描述三角形的类,提供计算周长和面积的方法

我们在类里面写的函数,通常称之为方法,它们基本上都是发给对象的消息。
但是有的时候,我们的消息并不想发给对象,而是希望发给这个类(类本身也是一个对象),
这个时候,我们可以使用静态方法或类方法。

静态方法 - 发给类的消息 —> @staticmethod—> 装饰器
类方法 - 发给类的消息 —>@classmethod—> 装饰器 —> 第一个参数(cls)是接收消息的类

class Triangle:
    """三角形"""

    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    # @classmethod
    # def is_valid(cls, a, b, c):
    #     return a + b > c and b + c > a and a + c > b

    @staticmethod
    def is_valid(a, b, c):
        return a + b > c and b + c > a and a + c > b

    def perimeter(self):
        return self.a + self.b + self.c

    def area(self):
        half = self.perimeter() / 2
        return (half * (half - self.a) * (half - self.b) * (half - self.c)) ** 0.5


if __name__ == '__main__':
    # 调用静态方法,判断三条边能否构成三角形
    if Triangle.is_valid(3, 4, 5):
        t = Triangle(3, 4, 5)
        print(t.perimeter())
        print(t.area())
    else:
        print('无效的边长,无法构造三角形对象')

方法二

    def __init__(self, a, b, c):
        if not Triangle.is_valid(a, b, c):
            raise ValueError('无效边长')   # raise 引发错误
        """初始化方法"""
        self.a = a
        self.b = b
        self.c = c


    # @classmethod  # (类方法)
    # def is_valid(cls, a, b, c):
    #     """判断三条边长能否构成三角形(静态方法)"""
    #     return a + b > c and b + c > a and a + c > b

    @staticmethod  
    # 静态方法(类方法) 写成Triangle.is_vaild()
    def is_valid(a, b, c):
        """判断三条边长能否构成三角形(静态方法)"""
        return a + b > c and b + c > a and a + c > b

    def perimeter(self):
        return self.a + self.b +self.c

    def area(self):
        half = self.perimeter() / 2
        return (half * (half - self.a) * (half - self.b) * (half - self.c))


if __name__ == '__main__':
    try:
        t = Triangle(1, 2, 3)
        print(t.perimeter())    # 当代码发生异常,这下面的都不执行#但except没有捕获到异常,就不会执行except的代码
        print(t.area())
    except ValueError as err:
        print(err)

注意点

  • try … except …:通过@staticmethod(静态方法)@classmethod (类方法)来设计一个is_valid()来发给类一个方法,来判断是否能构成三角形,不能构成就用 raise ValueError()来引发异常,并用except来捕获。

day05

一、

写一个扑克游戏,四个玩家参与,先洗牌,把牌发给四个玩家的手术

    • 属性: 花色,点数
    • 行为: 显示
  • 扑克
    • 属性:保存牌的列表
    • 行为:洗牌,发牌
  • 玩家
    • 属性:名字(昵称), 保存玩家手牌的列表
    • 行为:摸牌(把牌弄到自己的手上),整理手上的牌

通过这个例子,可以更加深入了解面向对象更多知识点

1.牌类:

class Card:

    def __init__(self, suite, face):
        self.suite = suite
        self.face = face

    def __str__(self):
        return self.show()
    # 没有这个方法就会出现默认地址

    def show(self):
        suites = {'S': '♠', 'H': '❤', 'C': '♣', 'D': '♦'}
        faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
        return f'{suites[self.suite]}{faces[self.face]}'

2.扑克类

注意:如果牌类和扑克类不在一个模块下,要导入该模块from…import…

class Poker:
    """扑克"""

    def __init__(self):
        # self.cards = []
        # for suite in 'SHCD':
        #     for face in range(1, 14):
        #         card = Card(suite,face)
        #         self.cards.append(card)
        self.cards = [Card(suite, face) for suite in 'SHCD' for face in range(1, 14)]
        self.counter = 0

    def shuffle(self):
        """洗牌"""
        self.counter = 0
        random.shuffle(self.cards)

    def deal(self) -> Card:
        """发牌"""
        card = self.cards[self.counter]
        self.counter += 1
        return card

    def has_more(self) -> bool:
        """是否还有牌"""
        return self.counter < len(self.cards)

3.玩家

from example02 import Poker


class Player:
    """玩家"""

    def __init__(self, nickname):
        self.nickname = nickname
        self.cards = []
        # 类里面的初始化

    def get_one_card(self, card):
        """摸一张牌"""
        self.cards.append(card)

    def arrange(self):
        """整理手上的牌"""
        self.cards.sort()

    def show(self):
        """显示玩家手上的牌"""
        print(self.nickname, end=':')
        for card in self.cards:
            print(card, end='')
        print()


def main():
    nicknames = ('喜羊羊', '美羊羊', '沸羊羊', '懒洋洋')
    players = [Player(nickname) for nickname in nicknames]
    poker = Poker()
    poker.shuffle()
    for _ in range(10):
        for player in players:
            card = poker.deal()
            player.get_one_card(card)
    # 显示四个玩家手上的牌
    for player in players:
        player.arrange()
        player.show()


if __name__ == '__main__':
    main()
  • sort(cmp=None, key=None, reverse=False)

  • sorted(iterable, cmp=None, key=None, reverse=False)

  • sort是容器的函数,用List的成员函数sort进行排序

  • sorted是Python的内建函数相同的参数,用built-in函数sorted进行排序

  • sorted(iterable,key=None,reverse=False),返回新的列表,对所有可迭代的对象均有效

  • sort(key=None,reverse=False) 就地改变列表 reverse:True反序;False 正序

修改遍历:

魔术方法(魔法方法)—> 有特殊用途和意义的方法
init —> 初始化方法,在调用构造器语法创建对象的时候会被自动调用
str —> 获得对象的字符串表示,在调用print函数输出对象时会被自动调用
repr —> 获得对象的字符串表示,把对象放到容器中调用print输出时会自动调用
—> representation
lt —> 在使用 < 运算符比较两个对象大小时会自动调用

如果一个变量的取值只有有限个选项,可以考虑使用枚举类型。
Python中没有定义枚举类型的语法,但是可以通过继承Enum类来实现枚举类型。
结论1:枚举类型是定义符号常量的最佳选择!!!
结论2:符号常量(有意义的名字)总是优于字面常量!!!

from  enum import Enum

class Suite(Enum):
    SPADR, HEART, CLUB, DIAMOD = range(4)

for suite in Suite:
    print(suite,suite.value)

运用知识点修改后

from enum import Enum


# 枚举类型
class Suite(Enum):
    SPADE, HEART, CLUB, DIAMOND = range(4)


class Card:
    """牌"""

    def __init__(self, suite, face):
        self.suite = suite
        self.face = face

    def __str__(self):
		return self.show()

    def __repr__(self):
		return self.show()

    def __lt__(self, other):
		return self.show()
    
    def __lt__(self, other):
        if self.suite == other.suite:
            return self.face < other.face
        return self.suite.value < other.suite.value


     def show(self):
         """显示"""
         suites = ['♠', '❤', '♣', '♦']
         faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
         return f'{suites[self.suite.value]}{faces[self.face]}'

扑克牌改成

def __init__(self):
    self.cards = [Card(suite, face)
                  for suite in Suite
                  for face in range(1, 14)]
    self.counter = 0

其余代码一致,加上Enum函数能很好的避开符号之类的排序

二、

如果要限制一个类的对象只能拥有某些属性,可以在类中使用__slots__魔法属性

class Student:
slots = (‘name’, ‘gender’)

继承:对已有的类进行扩展创建出新的类,这个过程就叫继承。
提供继承信息的类叫做父类(超类、基类),得到继承信息的类称为子类(派生类)。

继承是实现代码复用的一种手段,但是千万不要滥用继承。

继承是一种is-a关系。
a student is a person.
a teacher is a person.
a programmer is a person.

子类直接从父类继承公共的属性和行为,再添加自己特有的属性和行为,
所以子类一定是比父类更强大的,任何时候都可以用子类对象去替代父类对象。

Python中的继承允许多重继承,一个类可以有一个或多个父类。
如果不是必须使用多重继承的场景下,请尽量使用单一继承。

class Person:
    """人"""

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

    def eat(self):
        """吃饭"""
        print(f'{self.name}正在吃饭.')

    def play(self, game_name):
        """玩"""
        print(f'{self.name}正在玩{game_name}.')

    def introduce(self):
        """自我介绍"""
        sex = "男" if self.gender else "女"
        print(f'我叫{self.name}, 是一个{sex}人.')


class Student(Person):
    """学生"""

    def __init__(self, name, gender, grade):
        # 继承父类的属性
        super().__init__(name, gender)
        # 子类可以自己进行修改
        self.grade = grade

    def study(self, course_name):
        """学习"""
        print(f'{self.name}正在学习{course_name}.')


class Teacher(Person):
    """老师"""

    def __init__(self, name, gender, title):
        super().__init__(name, gender)
        self.title = title

    def teach(self, course_name):
        """教课"""
        print(f'{self.name}{self.title}正在讲授{course_name}.')


class Programmer(Person):
    """程序员"""

    def write_code(self, programming_language):
        """写代码"""
        print(f'{self.name}正在用{programming_language}写代码.')


stu = Student('王大锤', True, '五年级')
stu.study('语文')
stu.play('王者荣耀')
stu.eat()
stu.introduce()

teacher = Teacher('骆昊', True, '教授')
teacher.eat()
teacher.play('斗地主')
teacher.teach('Python程序设计')
teacher.introduce()

programmer = Programmer('白元芳', True)
programmer.eat()
programmer.play('吃鸡')
programmer.write_code('Python')
三、

两个类之间有哪些可能的关系???

  • ~ is-a关系:继承 —> 从一个类派生出另一个类
    a student is a person.
    a teacher is a person.

  • ~ has-a关系:关联 —> 把一个类的对象作为另外一个类的对象的属性
    a person has an identity card.
    a car has an engine.

    • (普通)关联强关联:

      ​ 整体和部分的关联,聚合和合成。

    • use-a关系:依赖 —> 一个类的对象作为另外一个类的方法的参数或返回值
      a person use a vehicle.

class Vehicle:
    """交通工具"""
    pass

# 子类继承父类:继承
class Horse(Vehicle):
    """马"""
    pass

# 继承
class Motobike(Vehicle):
    """摩托车"""
	# 关联:强关联
   def __init__(self):
       self.engine = Engine()


class Engine:
    """引擎"""
    pass


class Follower:
    """徒弟"""

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

   def do_work(self):
        pass


class MrTang:
    """唐僧"""
	# 拥有关系 --has关系:合成
     def __init__(self):
         self.followers = [Follower('孙悟空'), Follower('猪悟能'), Follower('沙悟净')]

     def drive(self, vehicle):
    	 """驾驶"""
       	  pass
    # has 关系,强关系,聚类
四、

工资(月薪)结算系统

面向对象编程的四大支柱:
抽象(abstraction):提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
封装(encapsulation):把数据和操作数据的函数从逻辑上组装成一个整体(对象)。
—> 隐藏实现细节,暴露简单的调用接口。
继承(inheritance):扩展已有的类创建新类,实现对已有类的代码复用。
多态(polymorphism):给不同的对象发出同样的消息,不同的对象执行了不同的行为。
—> 方法重写:子类对父类已有的方法,重新给出自己的实现版本
三类员工:
部门经理:固定月薪,15000元
程序员:计时结算月薪,每小时200元
销售员:底薪+提成,底薪1800元,销售额5%提成

录入员工信息,自动结算月薪

子类对父类已有的方法,重新给出自己的实现版本,这个过程叫做方法重写(override)。
在重写方法的过程中,不同的子类可以对父类的同一个方法给出不同的实现版本,那么该方法在运行时就会表现出多态行为。
“”"

from abc import abstractmethod


class Employee:

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

     @abstractmethod
     def get_salary(self):
         pass


class Manager(Employee):

     def get_salary(self):
         return 15000


class Programmer(Employee):

     def __init__(self, no, name):
         super().__init__(no, name)
         self.working_hour = 0

     def get_salary(self):
         return 200 * self.working_hour


class Salesman(Employee):

     def __init__(self, no, name):
         super().__init__(no, name)
         self.sales = 0

     def get_salary(self):
         return 1800 + 0.05 * self.sales


def main():
    emps = [
        Manager(1122, '刘备'), Programmer(2233, '诸葛亮'),
        Salesman(3344, '关羽'), Salesman(4455, '张飞'),
        Programmer(5566, '庞统'), Salesman(6677, '马超')
    ]
    for emp in emps:
        if type(emp) == Programmer:
            # 这里需要type()函数
            emp.working_hour = int(input(f'请输入{emp.name}本月工作时长: '))
        elif type(emp) == Salesman:
            emp.sales = float(input(f'请输入{emp.name}本月销售额: '))
        print(f'{emp.name}本月工资: {emp.get_salary()}元')

if __name__ == '__main__':
    main()

知识点:

  • 抽象(abstraction):提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象),需要from abc import abstractmethod来导入
  • 静态方法 - 发给类的消息 —> @staticmethod —> 装饰器
  • 类方法 - 发给类的消息 —> @classmethod —> 装饰器 —> 第一个参数(cls)是接收消息的类
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值