Python_week03

本文介绍了Python中的函数重构技巧,包括如何利用高阶函数简化代码,使用@property装饰器来增强类的安全性和便利性,以及如何利用静态方法和类方法提高代码效率。此外,还详细探讨了继承与多态的概念,并通过一个贪吃蛇游戏综合案例展示了面向对象编程的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数

我们在构建函数的时候会发现很多函数几乎完全相同,那么懒惰的程序员怎么能容忍这种事呢,如下面:

mylist = [1, 3, 4, 7]

def sum1(mylist):
"""计算列表元素之和"""
    total = mylist[0]
    for index in range(1,len(mylist)):
        total += mylist[index]
    return total
print(sum1(mylist))


def pro(mylist):
"""计算列表元素之积"""
    total = mylist[0]
    for index in range(1,len(mylist)):
        total *= mylist[index]
    return total
print(pro(mylist)

可以看出上面两个函数除了运算符号不同 其他代码完全一样,那么我们有没有办法可以一个函数搞定呢?当然可以啦!如下:

def calc(my_list, op):
"""对列表中的元素做运算"""
    total = my_list[0]
    for index in range(1, len(my_list)):
        total = op(total, my_list[index]) # op可以换成add 或者 mul
    return total
def add(x, y):
    return x + y

def mul(x, y):
    return x * y
print(calc(my_list, add)
print(calc(my_list, mul)
mylist = [1, 3, 4, 7]
def calc(my_list, op):
    """对列表中的元素做运算"""
    total = mylist[0]
    for index in range(1, len(mylist)):
        total = op(total, mylist[index]) # op可以换成add 或者 mul
    return total
def add(x, y):
    return x + y

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

print(calc(mylist, add))
print(calc(mylist, mul))
#如果觉得还要定义加法,乘法麻烦的话 还可以使用lambda函数(匿名函数)
print(calc(mylist, lambda x, y: x + y)

@property装饰器

之前我们讨论过Python中属性和方法访问权限的问题,虽然我们不建议将属性设置为私有的,但是如果直接将属性暴露给外界也是有问题的,比如我们没有办法检查赋给属性的值是否有效。我们之前的建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下所示。

class Person(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 访问器 - getter方法
    @property
    def name(self):
        return self._name

    # 访问器 - getter方法
    @property
    def age(self):
        return self._age

    # 修改器 - setter方法
    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        if self._age <= 16:
            print('%s正在玩飞行棋.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大锤', 12)
    person.play()
    person.age = 22
    person.play()
    # person.name = '白元芳'  # AttributeError: can't set attribute


if __name__ == '__main__':
    main()

静态方法和类方法

我们在类中定义的方法都是对象方法,也就是说这些方法都是发送给对象的消息。实际上,我们写在类中的方法并不需要都是对象方法。比如我们在创建三角形类的时候,在创建的时候我们是不是得先检查一下给的三条边是否能构成三角形,而这个时候的方法就是没有对象。那么就可以用静态方法解决这个问题,如下

from math import sqrt


class Triangle(object):

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

    @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 sqrt(half * (half - self._a) *
                    (half - self._b) * (half - self._c))


def main():
    a, b, c = 3, 4, 5
    # 静态方法和类方法都是通过给类发消息来调用的
    if Triangle.is_valid(a, b, c):
        t = Triangle(a, b, c)
        print(t.perimeter())
        # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
        # print(Triangle.perimeter(t))
        print(t.area())
        # print(Triangle.area(t))
    else:
        print('无法构成三角形.')


if __name__ == '__main__':
    main()

和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。

from time import time, localtime, sleep


class Clock(object):
    """数字时钟"""

    def __init__(self, hour=0, minute=0, second=0):
        self._hour = hour
        self._minute = minute
        self._second = second

    @classmethod
    def now(cls):
        ctime = localtime(time())
        return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)

    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

    def show(self):
        """显示时间"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)


def main():
    # 通过类方法创建对象并获取系统时间
    clock = Clock.now()
    while True:
        print(clock.show())
        sleep(1)
        clock.run()


if __name__ == '__main__':
    main()

类之间的关系

  • is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
  • has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
  • use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。

继承和多态

其实我们可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则。下面我们先看一个继承的例子。

class Person(object):
    """人"""

    def __init__(self, name, age):
        self._name = name
        self._age = age

    @property
    def name(self):
        return self._name

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        print('%s正在愉快的玩耍.' % self._name)

    def watch_av(self):
        if self._age >= 18:
            print('%s正在观看爱情动作片.' % self._name)
        else:
            print('%s只能观看《熊出没》.' % self._name)


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

    def __init__(self, name, age, grade):
        super().__init__(name, age)
        self._grade = grade

    @property
    def grade(self):
        return self._grade

    @grade.setter
    def grade(self, grade):
        self._grade = grade

    def study(self, course):
        print('%s的%s正在学习%s.' % (self._grade, self._name, course))


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

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

    @property
    def title(self):
        return self._title

    @title.setter
    def title(self, title):
        self._title = title

    def teach(self, course):
        print('%s%s正在讲%s.' % (self._name, self._title, course))


def main():
    stu = Student('王大锤', 15, '初三')
    stu.study('数学')
    stu.watch_av()
    t = Teacher('老王', 40, '老叫兽')
    t.teach('Python程序设计')
    t.watch_av()


if __name__ == '__main__':
    main()

子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。

# Python没有从语言层面支持抽象类的概念
# 我们可以通过abc模块拉制造抽象类的效果
# 在定义类的时候通过制定metaclass=ABCMeta 可以将类声明为抽象类
# 在抽象类是不能创建对象的 抽象类存在的意义是专门拿给其他类继承
# abc模块中还有一个包装器abstractmethod
# 通过这个包装器可以将方法包装为抽象方法 必须要求子类进行重写
from abc import ABCMeta, abstractmethod


class Staff(object):

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

    @property
    def name(self):
        return self._name

    @abstractmethod
    def get_salary(self):
        pass


class Manager(Staff):

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

    def get_salary(self):
        return 15000


class Programer(Staff):

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

    @property
    def working_hour(self):
        return self._working_hour

    @working_hour.setter
    def working_hour(self,working_hour):
        self._working_hour = working_hour if working_hour > 0 else 0

    def get_salary(self):
        return self._working_hour * 150


class Saler(Staff):

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

    @property
    def percentage(self):
        return self._sales

    def get_salary(self):
        return 1200 + self._sales * 0.05


def main():
    m = Manager('老王')
    p = Programer('小黑', 120)
    s = Saler('小红', 5000)
    x = [m, p, s]
    y = {}
    for staff in x:
        if isinstance(staff, Programer):
            staff.working_hour = int(input())
        # 同样是接收get_salary这个消息 但是不同的员工变现出了不同的行为
        # 因为三个子类都重写了get_salary方法 所以这个方法会变现出多态行为
        y[staff.name] = staff.get_salary()
    print(y)


if __name__ == '__main__':
    main()

综合案例

贪吃蛇

通过pycharm实现吃蛋,长大,撞壁,碰撞自身结束游戏的功能

from abc import ABCMeta, abstractmethod
from random import randint
import pygame

BLACK = (0, 0, 0)
FOOD_COLOR = (252, 168, 150)
GRENN_COLOR = (0, 255, 0)

UP = 0
RIGHT = 1
DOWN = 2
LEFT = 3


class GameObject(object, metaclass=ABCMeta):
    """游戏对象类"""
    def __init__(self, x=0, y=0, color=BLACK):
        self._x = x
        self._y = y
        self._color = color

    @abstractmethod
    def draw(self, screen):
        pass

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y


class Wall(GameObject):
    """围墙类"""
    def __init__(self, x, y, width, height, color=BLACK):
        super().__init__(x, y, color)
        self._width = width
        self._height = height
        self._color = color

    def draw(self, screen):
        pygame.draw.rect(screen, self._color,
                         (self._x, self._y, self._width, self._height), 4)

    @property
    def width(self):
        return self._width

    @property
    def height(self):
        return self._height


class Food(GameObject):
    """食物类"""
    def __init__(self, x, y, size, color=FOOD_COLOR):
        super().__init__(x, y, color)
        self._size = size
        self._color = color
        self._hidden = False

    def draw(self, screen):
        if not self._hidden:
            pygame.draw.circle(screen, self._color,
                               (self._x + self._size // 2, self._y + self._size // 2), self._size // 2, 0)
        self._hidden = not self._hidden


class SnakeNode(GameObject):
    """蛇节点类"""
    def __init__(self, x, y, size, color=GRENN_COLOR):
        super().__init__(x, y, color)
        self._size = size

    @property
    def size(self):
        return self._size

    def draw(self, screen):
        pygame.draw.rect(screen, self._color,
                         (self._x, self._y, self._size, self._size), 0)
        pygame.draw.rect(screen, BLACK,
                         (self._x, self._y, self._size, self._size), 1)


class Snake(GameObject):
    """蛇类"""
    def __init__(self):
        super().__init__()
        self._dir = LEFT
        self._nodes = []
        self._has_eat_food = False
        for index in range(5):
            node = SnakeNode(290 + index * 20, 250, 20)
            self._nodes.append(node)

    @property
    def dir(self):
        return self._dir

    def draw(self, screen):
        for node in self._nodes:
            node.draw(screen)

    def change_dir(self, new_dir): # 改变方向的方法,不能反方向移动
        if new_dir != self.dir and (self._dir + new_dir) % 2 != 0:
            self._dir = new_dir

    def move(self):  # 移动的方法
        head = self._nodes[0]
        snake_dir = self._dir
        x, y, size = head.x, head.y, head.size
        if snake_dir == UP:
            y -= size
        elif snake_dir == RIGHT:
            x += size
        elif snake_dir == DOWN:
            y += size
        else:
            x -= size
        new_head = SnakeNode(x, y, size)
        self._nodes.insert(0, new_head)
        if self._has_eat_food: # 吃到食物就不会去掉最后一节
            self._has_eat_food = False
        else:  # 没吃到食物就会都头部增加一节,尾部减少一节
            self._nodes.pop()

    def collide(self, wall): # 通过判断头部的坐标与墙的坐标是否重合判断是否撞墙
        head = self._nodes[0]
        return head.x < wall.x or head.x + head.size > wall.x + wall.width \
         or head.y < wall.y or head.y + head.size > wall.y + wall.height

    def eat_food(self,food): 
        if self._nodes[0].x == food.x and self._nodes[0].y == food.y:
            self._has_eat_food = True
            return True

    def body(self): # 通过对蛇的每个节点位置的遍历判断是否和头部重叠
        for node in self._nodes[4:]:
            if self._nodes[0].x == node.x and self._nodes[0].y == node.y:
                return True


def main():
    def refresh():
    """刷新窗口"""
        screen.fill((242, 242, 242))
        wall.draw(screen)
        food.draw(screen)
        snake.draw(screen)
        pygame.display.flip()

    def handle_key_event(key_event):
    """键的控制"""
        key = key_event.key 
        if key == pygame.K_F2:
            reset_game()
        elif key in (pygame.K_w, pygame.K_d, pygame.K_s, pygame.K_a):
        # 游戏中只有w a s d方向键有效, 其他键不做反馈
            if not game_over:
                if key == pygame.K_w:
                    new_dir = UP
                elif key == pygame.K_d:
                    new_dir = RIGHT
                elif key == pygame.K_s:
                    new_dir = DOWN
                elif key == pygame.K_a:
                    new_dir = LEFT
                snake.change_dir(new_dir)

    def creat_egg():
        row = randint(0, 29)
        col = randint(0, 29)
        return Food(10 + 20 * row, 10 + 20 * col, 20)

    def reset_game():
        nonlocal food, snake, game_over
        food = creat_egg()
        snake = Snake()
        pygame.event.clear()
        game_over = False

    def score():
        """窗口上显示计分"""
        fontbjecet = pygame.font.SysFont('SimHei', 32)
        WenBenKuangDuiXiang = fontbjecet.render('score: %d' % food_count, True, [0, 0, 0])
        KuangDuiXiang = WenBenKuangDuiXiang.get_rect()
        KuangDuiXiang.center = (510, 25)
        screen.blit(WenBenKuangDuiXiang, KuangDuiXiang)
        pygame.display.update()

    pygame.init()
    screen = pygame.display.set_mode((620, 620))  # 设置窗口大小
    pygame.display.set_caption('贪吃蛇')   # 设置窗口标题
    screen.fill((242, 242, 242))  # 填充窗口底色
    wall = Wall(10, 10, 600, 600)
    snake = Snake()
    food = creat_egg()
    pygame.display.flip()  # 刷新窗口
    clock = pygame.time.Clock()   # 导入窗口刷新帧数
    game_over = False
    running = True
    food_count = 0
    while running:
        for event in pygame.event.get():  # 从事件中获得事件
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                handle_key_event(event)
        if not game_over:
            refresh()
            snake.move()
            snake.eat_food(food)
        if snake.collide(wall) or snake.body():
            game_over = True
        if snake.eat_food(food):
            food = creat_egg()
            food_count += 10
        score()
        clock.tick(10)
    pygame.quit()


if __name__ == '__main__':
    main()

文件读写

文件是存在硬盘上的,我们需要给系统发送指令,才能再读写文件。在Python中我们使用内置函数open(),操作代码如下:

import time


# 异常机制 - 处理程序在运行过程中出现的意外状况的手段
# 因为不是所有的问题都能够在程序调试程序的时候就能发现

# 打开文件 --> 判断大小 --> 分配内存 --> 读取文件 --> 关闭文件


def main():
    try:
        # ./表示在当前路径下  ../表示上一级路径
        with open('../abc/hello.txt', 'r', encoding='utf-8') as fs:
            content = fs.readlines()  # 读整个文件
            for line in content:   # 一行一行的读文件
                print(line, end='')
                time.sleep(1)
            #print(content)
            #mylist = fs.readlines()  # 将文件读进一个列表容器
            #print(mylist)
    except FileNotFoundError as e:  # 把错误做别名处理
        print(e)  # 把错误信息打印出来
        print('指定的文件无法打开.')
    except IOError:
        print('读写文件是出现错误.')
    print('程序执行结束')


if __name__ == '__main__':
    main()
def main():
    try:
        with open('../abc/picture.jpg', 'rb') as fs1: # 读文件
            data = fs1.read()
            print(data)
            print(type(data))
        with open('../efg/view.jpg', 'wb') as fs2: # 将读取的内容写到另一个文件中
            fs2.write(data)
    except FileNotFoundError as e:  # 把错误做别名处理
        print(e)  # 把错误信息打印出来
        print('指定的文件无法打开.')
    except IOError:
        print('读写文件是出现错误.')
    print('程序执行结束')


if __name__ == '__main__':
    main()

当我们在文件读写的过程中就会发现,比如字符串,列表,字典等数据类型在某些程序中是无法识别的,那么为了解决这个问题引入json,就可以在相互传递数据了。
以下代码是在API接口扒取数据。

import requests
import json


def main():
    # request / response
    resp = requests.get('http://api.tianapi.com/meinv/?key=e219fef11afabfb22ea90c27f716fa5&num=1')
    mydict = json.loads(resp.text)
    print(mydict)
    for tempdict in mydict['newslist']:
        pic_url = tempdict['picUrl']
        resp = requests.get(pic_url)
        filename = pic_url[pic_url.rfind('/') + 1:]
        try:
            with open(filename, 'wb') as fs:
                fs.write(resp.content)
        except IOError as e:
            print(e)


if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值