【流畅的Python学习笔记】2023.4.21

news2024/10/7 8:26:25

此栏目记录我学习《流畅的Python》一书的学习笔记,这是一个自用笔记,所以写的比较随意

特殊方法(魔术方法)

不管在哪种框架下写程序,都会花费大量时间去实现那些会被框架本身调用的方法,Python 也不例外。Python 解释器碰到特殊的句法时,会使用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以两个下划线开头,以两个下划线结尾(例如 __getitem__)

命名元组

Python中有一种特殊的元组叫做命名元组,英文名叫namedtuple,按我的理解就是一个轻量级的“类”,它在collections库中,我将它类比为C语言的结构体,但是和结构体不同的是,它里面的元素可以按索引访问,自 Python 2.6开始,namedtuple 就加入到 Python 里,用以构建只有少数属性但是没有方法的对象,具体用法如下代码所示:

from collections import namedtuple

# 定义一个namedtuple类型People_Info,并包含id,name和phone_number属性。
People_Info = namedtuple('People_Info', ['id', 'name', 'phone_number'])

# 创建一个People_Info对象
people_info1 = People_Info(id='001', name='jack', phone_number='1333333XXXX')

# 通过使用_make方法直接对命名元组整体赋值(用List)
people_info2 = People_Info._make(['002', 'tom', '122XXXXXXXX'])

print(people_info1)
print(people_info2)

# 通过使用_replace方法修改(原本对象不变)命名元组的元素值以此生成新命名元组
# 注意:此处生成的是新命名元组,不是真的修改对象的值
people_info3 = people_info2._replace(id='003', name="mike")
print(people_info3)

# 通过.访问元组成员
print(people_info1.name)

# 通过下标访问元组成员
print(people_info2[0])

# 将元组转换成字典
people_info1_dict = people_info1._asdict()
print(people_info1_dict)

这段代码执行结果如下:
1

接下来我模仿官方示例写一段假面骑士decade卡盒的示例

__getitem__和__len__

魔术方法__getitem__支持按索引访问,即对对象做[]操作,而__len__则让对象支持Python中的len()方法,读取长度,具体用法我写了一个示例:
(Python 已经内置了从一个序列中随机选出一个元素的函数 random.choice)

from collections import namedtuple
from random import choice

# 骑士元组,骑士名字,模式列表,攻击方式列表
KamenRider = namedtuple('KamenRider', ['name', 'form_list', 'attack_list'])
# 将旧10年骑士的名字和其他信息录入到卡盒列表中
cards_list = [KamenRider(name='Kuuga', form_list=['Titan', 'Dragon', 'Pegasus'], attack_list=None),
              KamenRider(name='Agito', form_list=['Flame', 'Storm'], attack_list=['Gigant']),
              KamenRider(name='Ryuki', form_list=None, attack_list=['Advent', 'StrikeVent']),
              KamenRider(name='Faiz', form_list=['Axel'], attack_list=['AutoVajin', 'SideBasshar']),
              KamenRider(name='Blade', form_list=None, attack_list=['Metal', 'Mach']),
              KamenRider(name='Hibiki', form_list=None, attack_list=['OngekibouRekka', 'Onibi']),
              KamenRider(name='Kabuto', form_list=None, attack_list=['ClockUp']),
              KamenRider(name='Den-o', form_list=['Gun', 'Ax', 'Rod'],
                         attack_list=['Ore Sanjou!', 'Bokuni Tsurarete Miru?', 'Kotaewa Kiite Nai', 'Nakerude',
                                      'Utchari']),
              KamenRider(name='Kiva', form_list=['Garulu', 'Dogga', 'Basshaa'], attack_list=None),
              KamenRider(name='Decade', form_list=None, attack_list=['Slash', 'Blast', 'Illusion', 'Invisible'])]


# 卡面来打decade类
class Decade:
    # 当前是哪一个骑士(下标)
    now_index = 9

    # cards_list是卡盒列表
    def __init__(self, cards):
        self.cards = cards

    # len方法返回卡牌数量
    def __len__(self):
        return len(self.cards)

    # 变身方法
    def henshin(self):
        print("Henshin!")
        # 找到decade卡牌
        for item in self.cards:
            if item.name == 'Decade':
                print("Kamen Ride Decade!")
                return
        print("error!")
        return

    # __getitem__方法获取新的骑士驾驭
    def __getitem__(self, position):
        self.now_index = position
        return self.cards[position].name

    # 随机当前骑士的攻击驾驭
    def attack_ride(self):
        if self.cards[self.now_index].attack_list is not None:
            print("Attack Ride " + choice(self.cards[self.now_index].attack_list))

    # 随机当前骑士的模式驾驭
    def form_ride(self):
        if self.cards[self.now_index].form_list is not None:
            print("Form Ride " + self.cards[self.now_index].name + " " + choice(self.cards[self.now_index].form_list))

    # 当前骑士的终极攻击驾驭
    def final_attck_ride(self):
        str_f = ""
        # 循环几次,模仿腰带提示的结巴声音
        for i in range(0, 5):
            str_f = str_f + self.cards[self.now_index].name[0]
        print("Fianl Attack Ride " + str_f + self.cards[self.now_index].name + "!")

    # 写在类成员函数的随机假面驾驭
    def kamen_ride(self):
        print("Kamen Ride " + str(choice(self)) + "!")

    # 一次模式驾驭,一次攻击驾驭,一次终极攻击驾驭
    def do_attack(self):
        self.form_ride()
        self.attack_ride()
        self.final_attck_ride()

    # 神主牌模式,使用__getitem__方法,骑士卡牌可以迭代,故可以模拟出神主牌模式
    def complete_form(self):
        for card_name in self:
            print(card_name+" ", end='')
        print("\nFinal Kamen Ride Decade!")


player = Decade(cards_list)
player.henshin()
player.attack_ride()
player.attack_ride()
player.attack_ride()
player.final_attck_ride()
# 随机假面驾驭
print("Kamen Ride " + str(choice(player)) + "!")
player.form_ride()
player.attack_ride()
player.final_attck_ride()
player.kamen_ride()
player.do_attack()
player.complete_form()

效果:
3

倒数第三行到倒数第五行的输出也符合官方原作了(笑)
4
5

__contains__方法和排序

__contains__方法是⽤来判断集合中是否包含某个元素的⽅法,一个集合类型没有实现__contains__方法,那么 in 运算符就会按顺序做一次迭代搜索,由于Decade类是可以迭代的(有len和getitem魔术方法),所以可以直接用关键字in来判断,继续以刚才的例子做演示:

print('Decade' in player)

这里会显示True
再举一个人员信息的例子:

from collections import namedtuple

# 命名元组构造人员类
# 人员id,人员名字,人员年龄,人员工资
People = namedtuple("People", ['id', 'name', 'age', 'salary'])
peo_list = [People(1, 'Tom', 34, 6000), People(2, 'Jack', 3, 0), People(3, 'Mary', 21, 3000),
            People(4, 'Bob', 16, 1000), People(5, 'Sim', 61, 10000), People(6, 'Black', 50, 30000)]


# 人员信息类
class PeopleInfo:
    # people_list是人员列表
    # number是总人数
    def __init__(self, people_list):
        self.people_list = people_list
        self.number = len(people_list)

    def __getitem__(self, position):
        return self.people_list[position]

    def __len__(self):
        return self.number

    # __contains__方法用来查找人员信息中是否有幼儿,儿童,少年,青年,中年,老年
    def __contains__(self, item):
        # 用字典保存年龄范围
        # ()是元组
        age_range = {
            '幼儿': (0, 6),
            '儿童': (7, 14),
            '少年': (15, 18),
            '青年': (19, 39),
            '中年': (40, 59),
            '老年': (60, 200)
        }
        for people_item in self:
            if age_range[item][0] < people_item.age <= age_range[item][1]:
                return True
        return False

# score_high函数对人进行升序排序,按年龄占30%,工资占70%的顺序排序
def score_high(people_name):
    return people_name.age * 0.3 + people_name.salary * 0.7


people_info = PeopleInfo(peo_list)
# 迭代打印人员信息列表
for people in people_info:
    print(people)

print("\n")
# 按socre_high规则排序
for p in sorted(people_info, key=score_high):
    print(p)

# 查看人群里是否有幼儿,儿童,少年,青年,中年,老年
print('青年' in people_info)
print('老年' in people_info)
print('儿童' in people_info)

运行结果:
6

模拟数值类型、列表表达式和lambda表达式

lambda表达式

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

lambda 参数:操作(参数)
lambda [arg1[,arg2,arg3....argN]]:expression

列表表达式(列表推导)

[ 表达式 for 迭代变量 in 可迭代对象 if 条件表达式 ]

这样迭代遍历效率更高一些

模拟数值类型

模仿书中例子,写了一个多维度的欧几里得空间下的向量类,重载了加减乘除运算符,并且加了一些错误提示,实现了任意多维度向量的加减

import math


# 欧几里得空间下的向量类
class Vector:

    # 初始化一个多维欧几里得空间向量
    def __init__(self, *x):
        # 存储向量的列表
        self.vec_list = list(x)
        # 向量的维度
        self.dim = len(self.vec_list)

    # __repr__方便打印自我描述信息
    def __repr__(self):
        # 打印的时候中间用一个列表表达式对每一个元素打印并加逗号,显示出向量
        return f"Vector({', '.join(str(x) for x in self.vec_list)})"

    # __abs__方法,求向量模长
    def __abs__(self):
        # 使用列表表达式并不遍历列表,更加高效
        return math.sqrt(sum(i ** 2 for i in self.vec_list))

    # __len__方法,求向量的维度
    def __len__(self):
        return self.dim

    # __add__方法,向量加法,要求两个向量必须维度一致(原向量在左侧)
    def __add__(self, other):
        # 先检测加的是不是向量
        if isinstance(other, Vector):
            # 首先检测向量的维度是否相同,不同则抛出异常
            if self.dim != other.dim:
                raise ValueError("向量维度不同!\n")
            # 用*解包数组,让它们变成一个个参数
            return Vector(*[x + y for x, y in zip(self.vec_list, other.vec_list)])
        else:
            raise TypeError("向量只能和向量相加!")

    # __radd__方法,向量加法,要求两个向量必须维度一致(原向量在右侧)
    def __radd__(self, other):
        # 先检测加的是不是向量
        if isinstance(other, Vector):
            # 首先检测向量的维度是否相同,不同则抛出异常
            if self.dim != other.dim:
                raise ValueError("向量维度不同!\n")
            # 用*解包数组,让它们变成一个个参数
            return Vector(*[x + y for x, y in zip(self.vec_list, other.vec_list)])
        else:
            raise TypeError("向量只能和向量相加!")

    # __mul__实现向量的标量乘法和向量的点乘(原向量在左侧)
    def __mul__(self, other):
        # 如果other是整型或浮点型的数字类型,就执行向量标量乘法
        if isinstance(other, (int, float)):
            return Vector(*[i * other for i in self.vec_list])
        elif isinstance(other, Vector):
            # 首先检测向量的维度是否相同,不同则抛出异常
            if self.dim != other.dim:
                raise ValueError("向量维度不同!\n")
            return sum(x * y for x, y in zip(self.vec_list, other.vec_list))
        raise TypeError("无法计算给定类型的向量")

    # __rmul__实现向量的标量乘法和向量的点乘(原向量在右侧)
    def __rmul__(self, other):
        # 如果other是整型或浮点型的数字类型,就执行向量标量乘法
        if isinstance(other, (int, float)):
            return Vector(*[i * other for i in self.vec_list])
        elif isinstance(other, Vector):
            # 首先检测向量的维度是否相同,不同则抛出异常
            if self.dim != other.dim:
                raise ValueError("向量维度不同!\n")
            return sum(x * y for x, y in zip(self.vec_list, other.vec_list))
        raise TypeError("无法计算给定类型的向量")

    # __truediv__实现向量除一个标量
    def __truediv__(self, other):
        if isinstance(other, (int, float)):
            if other == 0:
                raise ValueError("不能除0!")
            return Vector(*[i / other for i in self.vec_list])
        raise TypeError("无法计算给定类型的向量")

    # __rtruediv__告诉用户向量无法做除数
    def __rtruediv__(self, other):
        raise TypeError("向量无法做除数")

    # __bool__如果一个向量的模是 0,那么就返回 False,其他情况则
    # 返回 True。
    def __bool__(self):
        return bool(abs(self))


vec1 = Vector(3, 3, 3, 3)
vec2 = Vector(2, 2, 2, 2)
print(vec1)
print(abs(vec1))
print(len(vec1))
print(vec2 + vec1)
print(3 * vec1)
print(vec2 * 10)
vec3 = Vector(1, 1, 1)
print(len(vec3))
print(bool(vec3))
vec4 = Vector(0, 0, 0)
print(bool(vec4))



运行结果:
7

ord方法和列表表达式,map,filter等的练习

ord方法返回一个字符的ascii码,fliter(过滤条件函数,迭代器)

symbols = 'abcdef'
codes = [ord(symbol) for symbol in symbols]
print(codes)

8
P.S Python 会忽略代码里 []、{} 和 () 中的换行,因此如果你的代码里有多行的列表、列表推导、生成器表达式、字典这一类的,可以省略不太好看的续行符 \。

symbols = 'abcdef'
codes = [ord(symbol) for symbol in symbols if ord(symbol)>100]
print(codes)

9

symbols = 'abcdef'
codes = list(filter(lambda c: c > 100, map(ord, symbols)))
print(codes)

10

笛卡尔积

笛卡尔积的具体作用如图
11
集合X={red,green,yellow},集合Y={A,B}
X与Y做笛卡尔积就是上面这个结果

from collections import namedtuple
Tshirts = namedtuple("Tshirts",['color', 'type'])
# 笛卡尔积测试代码
color_list = ["red", "green", "yellow"]
type_list = ['A', 'B']
result_list = [Tshirts(color,type) for color in color_list for type in type_list]
for item in result_list:
    print(item)

12

生成器表达式

生成器表达式的语法跟列表推导差不多,只不过把方括号换成圆括号而已。

symbols = 'abcdef'
codes = list(ord(symbol) for symbol in symbols)
number = sum(ord(symbol) for symbol in symbols)
print(codes)
print(number)

13
生成器表达式比较节省内存

from collections import namedtuple
Tshirts = namedtuple("Tshirts",['color', 'type'])
# 生成式表达器生成笛卡尔积测试代码
color_list = ["red", "green", "yellow"]
type_list = ['A', 'B']
result_list = (Tshirts(color,type) for color in color_list for type in type_list)
for item in result_list:
    print(item)

14

实际上还能使用 %符号来给字符串做格式化输出

from collections import namedtuple

Tshirts = namedtuple("Tshirts", ['color', 'type'])
# 生成式表达器生成笛卡尔积测试代码
color_list = ["red", "green", "yellow"]
type_list = ['A', 'B']
result_list = ('%s %s' % Tshirts(color, type) for color in color_list for type in type_list)
for item in result_list:
    print(item)

15
用到生成器表达式之后,内存里不会留下一个有 6 个组合的列表,因为生成器表达式会在每次 for 循环运行时才生成一个组合。如果要计算两个各有 1000 个元素的列表的笛卡儿积,生成器表达式就可以帮忙省掉运行 for 循环的开销,即一个含有 100 万个元素的列表。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/444906.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】matplotlib设置图片边缘距离和plt.lengend图例放在图像的外侧

一、问题提出 我有这样一串代码&#xff1a; import matplotlib.pyplot as plt plt.figure(figsize (10, 6)) " 此处省略代码 " legend.append("J") plt.legend(legend) plt.xlabel(recall) plt.ylabel(precision) plt.grid() plt.show()我们得到的图像…

KMP算法原理原来这么简单

我觉得这句话说的很好&#xff1a; kmp算法关键在于&#xff1a;在当前对文本串和模式串检索的过程中&#xff0c;若出现了不匹配&#xff0c;如何充分利用已经匹配的部分&#xff0c;来继续接下来的检索。 暴力解决字符串匹配 暴力解法就是两层for循环,每次都一对一的匹配&…

面试官:“请描述一下Android系统的启动流程”

作者&#xff1a;OpenGL 前言 什么是Android启动流程呢&#xff1f;其实指的就是我们Android系统从按下电源到显示界面的整个过程。 当我们把手机充好电&#xff0c;按下电源&#xff0c;手机会弹出相应启动界面&#xff0c;在等了一段时间之后&#xff0c;会弹出我们熟悉的主…

AI数据标注工程师这个职业怎么样?

本篇文章主要讲解ai数据标注工程师这个职业的具体情况和相关的职业前景 作者&#xff1a;任聪聪 日期&#xff1a;2023年4月18日 数据是ai的灵魂&#xff0c;自然界中相对应的数据都活多少存在不准确、杂乱、无效等属性&#xff0c;需要人为进行收集、整理、分类和处理。其中ai…

Linux 内核原理摘录

文章目录 一、Linux 内核设计与实现1、进程管理&#xff08;1&#xff09;调度2、内核数据结构&#xff08;1&#xff09;kfifo 3、中断 一、Linux 内核设计与实现 本章主要用来摘录《Linux 内核设计与实现》一书中学习知识点&#xff0c;其基于 Linux 2.6.34 。 1、进程管理 …

PowerShell install go+caddy+filebrowser+nssm 实现部署文件系统

filebrowser filebrowser 是一个使用go语言编写的软件&#xff0c;功能是可以通过浏览器对服务器上的文件进行管理。可以是修改文件&#xff0c;或者是添加删除文件&#xff0c;甚至可以分享文件&#xff0c;是一个很棒的文件管理器&#xff0c;你甚至可以当成一个网盘来使用。…

找高清视频素材,就上这6个网站。

推荐6个高清视频素材库&#xff0c;免费下载&#xff0c;建议收藏~ 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库可以找到设计、办公、图片、视频、音频等各种素材。视频素材就有上千个&#xff0c;全部都很高清&#xff0c;站内可以按标签分类查找…

某医院访问医保系统慢流量分析案例

背景 我们已将NetInside流量分析系统部署到某市人民医院的机房内&#xff0c;使用流量分析系统提供实时和历史原始流量。本次分析重点针对访问外网医保系统性能进行分析&#xff0c;以供安全取证、性能分析、网络质量监测以及深层网络分析。 分析时间 报告分析时间范围为&am…

【Linux】进程间通讯

前提知识 进程间具有独立性 &#xff0c;现在我们要打破独立性&#xff0c;去通讯&#xff0c;所以 通讯的成本一定很高。 又时候又需要多进程协同完成某种业务内容 &#xff0c;例如以前&#xff1a; cat file | grep “hello",这就是两个进程之间的通讯。 通讯的本质就是…

【一文学会MQTT协议和mosquitto】

一文学会MQTT协议和mosquitto 一.、MQTT的基本概念二、mosquittomosquitto基本概念常用API 三、MQTT测试EMQX 一.、MQTT的基本概念 MQTT是一种基于发布/订阅模式的协议&#xff0c;其中发布者发布消息&#xff0c;订阅者订阅感兴趣的主题&#xff08;topic&#xff09;&#x…

QGIS--开发OpenSCENARIO动态场景(二)--安装插件

1.下载并安装ad_map&#xff08;无需构建&#xff09;&#xff1a; 1&#xff09;ad_map插件&#xff1a; https://github.com/carla-simulator/map/releases 下载第一个&#xff1a;ad_map_access_qgis.zip 2&#xff09;导入插件&#xff1a; 从MenuBar的Plugins >…

YOLOv8 更换主干网络之 FasterNet

论文地址:https://export.arxiv.org/pdf/2303.03667v1.pdf 为了设计快速神经网络,许多工作都集中在减少浮点运算(FLOPs)的数量上。然而,作者观察到FLOPs的这种减少不一定会带来延迟的类似程度的减少。这主要源于每秒低浮点运算(FLOPS)效率低下。并且,如此低的FLOPS主要…

ThreadLocal InheritableThreadLocal TransmittableThreadLocal的使用以及原理

ThreadLocal 每个线程向ThreadLocal设置值&#xff0c;再取值&#xff0c;实现线程之间的隔离 public class ThreadLocalCase1 {private static ThreadLocal<Integer> threadLocal new ThreadLocal<>();public static void main(String[] args) {Random random …

Postman接口与压力测试实例

Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。它提供功能强大的 Web API & HTTP 请求调试。 1、环境变量和全局变量设置 环境变量可以使用在以下地方&#xff1a; URLURL paramsHeader valuesform-data/url-encoded valuesRaw body contentHelper f…

MySQL学习笔记第一天

第02章 MySQL环境搭建 1.MySQL的卸载 步骤1&#xff1a;停止MySQL服务 在卸载之前&#xff0c;先停止MySQL8.0的服务。按键盘上的“Ctrl Alt Delete”组合键&#xff0c;打开“任务管理器”对话框&#xff0c;可以在“服务”列表找到“MySQL8.0”的服务&#xff0c;如果现…

AIGC: Midjourney和Stable Diffusion在大厂中的应用

AIGC: Midjourney和Stable Diffusion在大厂中的应用和教程​http://www.webhub123.com/#/home/detail?projectHashid51631966&ownerUserid21336964 收录效果如下 登录后即可一键拥挤收藏以下所有文章网址到我的收藏夹&#xff0c;网站帮你简单高效地管理你的网络收藏&…

Memtiter-benchmark源码解析4memtier_benchmark.cpp源代码解读

run_benchmark 程序入口 First , create the cg_thread instances in the vector threads and then call each cg_thread’s prepare() function of them. Subsequently, launch each thread in the vector threads cg_thread m_base event_base_new() // line 528 一个cli…

移动端手机网页适配iPad与折叠屏设备

采用的网页适配方案&#xff1a;移动端页面px布局适配方案&#xff08;viewport&#xff09; 产生此问题的原因 由于手机与平板等设备宽高比差异导致页面展示不全或者功能按钮展示在视口之外点击不到。 简单来说就是我们的页面都是瘦长(即高大于宽)的&#xff0c;而折叠屏等设…

【计算机专业应届生先找培训还是先找个工作过渡一下?】

计算机专业应届生先找培训还是先找个工作过渡一下&#xff1f; 计算机应届生是先培训还是先工作&#xff0c;这个问题应该困扰了很多专业技能一般的同学&#xff0c;尤其是学历方面还没有优势的普通本专科院校。都说技术与学历优秀的人进大厂&#xff0c;技术一般学历优秀的人能…

@ComponentScan自动扫描组件并指定扫描规则

1.使用注解配置包扫描 1.1.创建相关类 分别创建BookDao、BookService、BookServiceImpl以及BookController这三个类&#xff0c;并在这三个类中分别添加Repository、Service、Controller注解 BookDaopackage com.tianxia.springannotation.dao;import org.springframework.s…