Dive into Python
Content
书写
- Python 有个有意思的点,对于语句,并不要求用 “{}” 围起来,各个代码模块间用缩进来规范。
- 语句过长时,为了代码美观,需要适当截断。Python 单一语句换行依靠”\“链接
- 说来惭愧,我鸡梨骨鹿说了一堆,最后提交文档的时候自己格式疯狂出错 /// 啊啊啊
PEP8 编码规范(尽可能遵守、不强求)
- 缩进:使用 4 个空格来进行缩进,不要使用制表符(Tab)。
- 行宽:每行代码尽量不超过 79 个字符,以便于在小屏幕上阅读。
- 空格的使用:
- 在逗号、冒号、分号后使用空格,但不要在括号前使用。
- 赋值运算符和比较运算符两边都应添加空格,但在传递关键字参数或默认参数值时不加空格。
- 命名规则:
- 函数、变量用小写字母编写,单词之间用下划线分隔(snake_case)。
- 类名使用首字母大写的每个单词拼接(CapWords 或称 CamelCase)。
- 模块级别的常量全用大写字母,单词之间用下划线分隔。
- 导入规则:
- 导入应该总是放在文件顶部。
- 应该分行导入,避免同一行导入多个库或模块。
- 标准库模块、第三方模块以及自定义模块导入应该分组写在不同的部分,每组之间用空行隔开。
变量
变量在 Python 中用来存储数据值。 Python 有五个标准数据类型:
- 数字
- 字符串(环绕单引号、双引号)
- 列表(nb)
- 字典(键与值对应,如同查字典)
- 元组(可以看作不可修改的列表)
Python 是动态类型语言,这意味着你不需要显式声明变量的类型,Python 在运行时会自动推断。
- 特别之处: Python 的变量可以在程序运行时改变类型。
- 注意:直接变换变量的数据类型素质很差,还是建议像 c 语言一样调用函数来做到 “显式类型转换”
x = 10 # x是整数类型,python中数字包含“int、long、float、complex“
print(x)
x = "hello" # 现在x是字符串类型,字符串由数字、字母、下划线组成
print(x)
# 而在c语言中:
# int a;
# 声明变量a为整型,并且后续变量a不允许修改为其他类型
2
3
4
5
6
7
8
类型标注(不强求)
Python 是一种动态类型语言,意味着不需要在编写代码时声明变量的类型。
然而,从 Python3.5 开始,引入了类型标注,使得开发者(可选地)指定变量、函数的参数以及返回值的类型。类型标注的主要目的是为了提高代码的可读性和可维护性,并且可以被静态类型检查工具(如 MyPy)使用,来在代码运行前发现潜在的错误。
def greet(name: str) -> str: #第一个“str”表示传入参数应该是str类型;
return f"Hello, {name}" #第二个“->str"表示函数返回值是str类型
def add_numbers(a: int, b: int) -> int:
return a + b
2
3
4
5
算数运算符
除去加减乘除外,python 有以下运算符:
- “/”:保留小数
- “%”:取除法运算的余数
- “a**b”:返回 a 的 b 次幂
- “//”:向下取整
print(5 / 2)
print(5 % 2)
print(5 ** 2)
print(5 // 2)
2
3
4
赋值、比较、位运算符
整体上与 c 语言一致,没有什么特别的;
唯一有区别的可能是 “>>" 运算符,在 python 中只有逻辑右移(高位补 0),没有算数右移(高位补有效值)
逻辑运算符
- and(&&):两边同时为真时返回右边表达式的计算值,否则返回假
- or(||):左边为真则返回左边的计算值,否则返回右边的计算值
- not(!):真返回假,假返回真
- 注意:真与 “1” 等价,假与 “0“等价
print(5 and (2 < 3)) # 同时为真,返回右边的True,注:2<3这个不等式为真
print(0 or (1 - 2)) # 左边为假,返回右边的计算值
print(not 1)
2
3
4
5
成员运算符
- a in b: 如果 a 在 b 中,则为真,否则假
- a not in b:与上面相反
a = 10
b = [10, 2, 3]
print(a in b)
print(a not in b)
2
3
4
5
控制流
控制流指的是决定代码执行顺序的语句,主要包括:
- if 条件语句:程序按照各个条件的先后顺序判断,满足条件后直接执行条件下的代码,不再判断
- for 循环:在变量满足 "in" 后条件的情况下,不断执行主体,变量会自动迭代
- while 循环:,在满足条件的情况下,不断执行主体
- break 语句:终止本次循环,跳出整个循环
- continue 语句:终止本次循环,直接进入下一次循环
- 注意:循环是可以嵌套的
# if语句
# elif与else都不是必须的
print("对于if语句,输出如下:")
i = 10
if i < 20:
print("i is less than 20")
elif i < 30:
print("i is less than 30")
else:
print("i is bigger than 30")
# for循环
print("\n对于for语句,输出如下:")
for i in range(1, 3):
print(i)
# while循环
print("\n对于while语句,输出如下:")
i = -2
while i < 0:
print(i)
i += 1
# break语句
print("\n对于加入了break语句的循环,输出如下:")
flag = 0
i = 0
while i < 5:
flag += 1
print(flag)
if flag > 2:
print("break happens")
break
i += 1
# continue语句
print("\n对于加入了continue语句的循环,输出如下:")
flag = 0
i = 0
while i < 5:
flag += 1
if flag < 3:
print("continue happens")
continue
else:
print(flag)
i += 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
列表、字典、元组
- 列表:用 “[]” 标识,类似动态数组,可以存储任意类型的对象。支持增删改,“+” 表示列表连接运算,“*” 标识列表重复运算
- 字典:用 “{}” 标识,内部之间没有顺序。存储 “键:值 “对的数据结构,” 键 “被映射到 “值”。支持增删改,可以通过 “键” 来访问 “值”
- 元组:用 “()” 标识,不可变的序列,可以看作是一个只读的列表。
- 特别之处: 列表和字典的操作非常灵活且功能强大。此外,列表和字典有众多内置操作函数,在此不一一举例(太多了)
# 列表
print("对于列表,输出如下:")
# 创建了一个包含多类型数据对象的列表
list = [1, 'string', [1]]
# 增加了一个元素
list.append("addtion")
list2 = ["the second part"]
# 当索引为负数时,从后往前访问
print(list[-1])
# 区间写法,访问第一二个元素(左闭右开)
print(list[0:2])
# 删除一个元素
del list[-1]
# 合并两个列表
list += list2
print(list)
# 字典
print("\n对于字典,输出如下:")
# 创建了一个字典,有三对键:值
dict = {'key': 'value', 1: 'hello', 2: 'hello', 3: 333}
# 修改了键“1”所对应的值
dict[1] = 'not hello'
# 增加了新的键值对
dict['new'] = 'add'
# 打印键“key”的值
print(dict['key'])
# 打印整个字典
print(dict)
# 元组
print("\n对于元组,输出如下:")
# 创建了元组
tuple = ("hello", 2)
# 打印第二个元素
print(tuple[1])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
字符串
字符串用于在 Python 中处理文本数据,例如单词、句子。
python 自带一些操作字符串的函数
特别之处: Python 的字符串是不可变的,意味着一旦创建,它们的内容不能更改。但是可以延长。字符串在一定程度上可以当作列表使用
# 变量s被赋值为字符串"Hello World"
s = "Hello World"
# 使用了类似列表的访问方式,打印第1个字符(大多数语言中第一位都从0开始)
print(s[0])
# 展示了一个字符串操作函数
print(s.lower()) # 转换为小写
# 延长了字符串s
s += ',hello hdu'
print(s)
2
3
4
5
6
7
8
9
10
11
12
13
生成器
是一种序列,每次调用时只按照顺序取出一个,直到取完为止
最常见的有
for i in range(0,5):
中的 "range ()"yield 是与生成器紧密相关的关键字,在返回 yield 后面的值之后暂停,而不是 return 那样全部返回
def double_numbers(n):
num = 1
while n > 0:
yield num
num *= 2
n -= 1
# 使用生成器,此时double_numbers(5)已经是一个生成器序列了,
# 但我们只能通过循环来获得之中的值(因为生成器序列每次只取出一个值)
# 每次for循环会调用一次yield,并生成一个num/number,
# 而后生成器序列double_numbers(5)暂停,下一次for循环会接着yield之后的部分继续执行,直到再次遇到yield
for number in double_numbers(5):
print(number)
def sub_generator():
yield 1
yield 2
yield 3
# yield from 可以让你在一个生成器函数中直接从另一个生成器或可迭代对象中产生值,而无需显式地使用类似for循环来逐一yield
def main_generator():
yield from sub_generator()
yield 4
yield 5
for value in main_generator():
print(value)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
函数
很多时候,我们需要一系列指令在程序多个地方使用,为了方便,通常将这一些指令 “打包” 做成函数。 与数学中而函数一样,分为自变量(参数)与因变量(函数结果)
- 特别之处: Python 支持高阶函数,即可以接受其他函数作为参数或者返回一个函数。
函数写法
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数内容以冒号起始,并且缩进。
- “return 表达式” 结束函数(非必须),选择性地返回一个值给调用方。不带表达式的 return 相当于返回 None。
def function_name(element1,...):
expressions & sentences
return something
2
3
调用函数
使用函数名与恰当的参数即可
匿名函数 lambda
lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。
lambda [arg1 [,arg2,.....argn]]:expression
变量作用域
对于任何变量,都有一个生效的范围,例如全局创建的 a,在程序结束运行前会一直存在,而函数中创建的 b, 在函数结束时就会销毁
# 制作了函数"greet()",需要接收一个输入"name"
def greet(name):
return "Hello " + name
# 函数中也可以再次调用函数,函数"print_greet()"不需要输入
def print_greet():
print(greet("HDU\n"))
# 执行print_greet函数
print_greet()
# lambda函数举例,在以下例子中,函数"make_incrementor()"的输入为"n"
def make_incrementor(n):
return lambda x: x + n
# 下面这行将"n"绑定为2,但仍然缺少lambda语句中定义的未知数x
f = make_incrementor(2)
# 该调用向f传递了2作为"x"
print(f(42))
# 也可以不借助f,直接调用原函数,这时需要分步传入n与x
make_incrementor(2)(43)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
标准库
Python 的标准库提供了大量预构建的模块和功能,支持从文件操作到网络通信等多种任务。
- 特别之处:Python 的标准库非常全面,几乎涵盖所有常见的编程任务,并且其标准库的接口设计非常直观。
创建模块
可以自己写一个 name.py
的文件,在合适的文件路径下,这个 “name” 便等同于标准库的名字,可以借助 import
导入自己定义的函数
# 导入整个标准库:datetime,并取了别名dt
import datetime as dt
# 导入单个模块的写法:from library import module
now = dt.datetime.now()
independence_day = dt.datetime(2023, 1, 1)
time_difference = now - independence_day
print(time_difference)
2
3
4
5
6
7
8
9
10
11
12
文件处理
本章节细节较多、函数较多,本文只做简单介绍
文件的打开与关闭
object = open(file_name [, access_mode][, buffering])
:打开了文件file_name
,选择了打开模式(例如读、写)access_mode
(可选),是否设置缓冲区buffering
(可选)。该文件的调用接口被设置为 “object”object.close()
:关闭 “object” 指向的文件
读写
object.write(string)
: 朝文件末尾添加字符串 “string”,该函数不自带换行符object.read([count])
: 从文件开头读取 “count” 个字节(可选)的数据
当前字节的定位
在我写入或者读取一定字节和后,文件中的指针会移动
object.tell()
: 指明当前进度object.seek(offset [,from])
: 从 “from” 处(可选的,012 分别表示开头当前结尾),接着偏移 “offset” 字节,将指针指向这个新位置
- 特别之处:Python 的文件处理可以直接处理二进制文件
异常处理
异常是程序运行中出现的影响代码执行的时间,在 python 中,异常是一个对象 (所以可以自己定义)
遇到异常时,程序通常经过 “捕捉,处理,继续” 三个阶段的操作,这也是一种控制流(所以也可以嵌套)
try: # 程序首先“尝试“以下代码,并试图在这部分代码中捕捉错误
fh = open("testfile", "r")
fh.write("这是一个测试文件,用于测试异常!!")
except IOError: # 如果出现“意外”,并且该异常类型与所检查的相同(本例中为IOError,则执行“except”下的语句
print("error!") # 如果异常类型与我们试图检查的不符,则跳出语句出现报错
else: # 如果没有出现异常,则执行else中的语句
print("success")
fh.close()
try:
fh = open("testfile", "w")
fh.write("这是一个测试文件,用于测试异常!!")
finally: # 无论是否出现异常,finally所包含的语句都会在最后被执行
print("actually no error!")
try:
a = 5
if a > 1:
raise Exception # 使用raise可以主动弹出异常
else:
print("safe")
except Exception:
print("oops")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
装饰器
装饰器是 Python 的一个强大功能,它允许用户在不修改函数代码的情况下增加函数功能。听起来很魔幻,过去数学中的函数最多只能函数嵌套,是不能在外层函数中修改内层函数的。笔者装饰器写的很少,建议请教柠檬大师 qwq
- 装饰器本质上是一个函数,它接受一个函数作为参数并返回一个新的函数。它在定义函数的时候提供了一个灵活的方式来 “包装” 函数。
# 定义了装饰器函数“debug”
def debug(func):
# 内部新定义的函数,输入为系统传入的参数数量args与每个参数kwargs
def wrapper(*args, **kwargs):
# 装饰效果:打印语句并让原函数计算结果加1
print("this is result with decorator:")
result = func(*args, **kwargs) + 1
return result
# 让装饰器函数调用wrapper函数
return wrapper
# 装饰器的使用方法,可以对一个函数使用多个装饰器
@debug
def add(x, y):
return x + y
def add_normal(x, y):
print("the normal one:")
return x + y
print(add(5, 7))
print(add_normal(5, 7))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
面向对象编程(OOP)
还是来了!很重要的一部分,希望我写的不是很烂。。。 Python 支持面向对象的编程风格,允许开发者通过类和对象来组织代码。 简单来说,就是定义了一个类型,该类型包含一些成员、函数。根据类型创建的变量叫做类。
- 特别之处:Python 的类定义相比其他语言(如 Java 或 C++)更为简洁,不需要复杂的语法结构,使得代码更容易编写和理解。
# 创建了类“Car”
class Car:
# “self”是一个指针,指向自己(当前对象)
# 该类具有成员brand、model、year
# 以下函数是类创建新的对象时自动调用的初始化函数,需要传入对应的参数
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
# 定义了一个函数,用于打印当前对象的全部信息
def display_info(self):
return f"{self.year} {self.brand} {self.model}"
# 创建 Car 类的实例,该对象名为my_car
my_car = Car("Tesla", "Model S", 2024)
# 调用类函数常常使用“.”
print(my_car.display_info())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
类的继承
Python 支持类的继承,允许一个类继承另一个类的方法和属性。 多重继承允许一个类继承多个父类。
class Animal: # 本例中承担“基类”的角色
def speak(self):
print("Animal speaks")
class Mammal(Animal): # 继承自Animal
def walk(self):
print("Mammal walks")
class Cute: # 本例中承担“基类2”的角色
def kiss(self):
print("kiss you")
class Dog(Mammal, Cute): # 同时继承了Mammal和Cute,多重继承
def wag_tail(self):
print("Dog wags tail")
doggie = Dog()
doggie.speak()
doggie.wag_tail()
doggie.walk()
doggie.kiss()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
特殊类
- 抽象基类(Abstract Base Classes, ABCs):用于创建其他类必须继承的抽象类,主要是为了定义通用的 API。抽象方法定义在抽象基类中,使用 @abstractmethod 装饰器标记。子类必须实现抽象方法才能被实例化。
- 枚举类:用于定义常量集合的类。使用 Enum 类来创建它们。
# 抽象类
from abc import ABC, abstractmethod
class Shape(ABC): # 抽象类Shape继承自模块abc里面的抽象基类ABC
@abstractmethod # 表明函数“area”是抽象方法
def area(self):
pass # 无实际意义,占位
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self): # 实现了area函数,允许被实例化
return 3.14 * self.radius ** 2
# 使用 Circle 类
circle = Circle(5)
circle.area()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 枚举类
from enum import Enum
class Color(Enum): # 枚举类Color继承自模块enum中的Enum类
RED = 1
GREEN = 2
BLUE = 3
# 枚举的访问方式
print(Color.RED)
print(Color.RED.name)
print(Color.RED.value)
2
3
4
5
6
7
8
9
10
11
12
13
14
类的方法
- 实例方法:类的普通方法,它们的第一个参数总是 self,表示类实例本身。实例方法可以访问和修改对象的状态。(见例子:Car)
- 特殊方法:由双下划线包围的方法(如 init、str 等),用于提供对类的内建操作的支持,比如迭代、运算符重载、访问属性等。
- 类方法:用
@classmethod
装饰器标记的,它们的第一个参数是 cls,代表类本身。类方法可以访问和修改类状态。 - 静态方法:用
@staticmethod
装饰器标记的,不接受 self 或 cls 参数。这种方法既不访问实例状态也不访问类状态,它们是一种与类紧密联系的普通函数。 - 抽象方法:定义在抽象基类中,使用
@abstractmethod
装饰器标记。子类必须实现抽象方法才能被实例化。(见例子:Circle)
# 特殊方法
class Book:
def __init__(self, title):
self.title = title
def __str__(self): # 将这个
return f"Book: {self.title}"
name = Book("Dream")
print(name)
2
3
4
5
6
7
8
9
10
11
# 类方法
class Employee:
company = "XYZ Corp"
@classmethod # 表明函数“display_company“是类方法,需要传入类本身
def display_company(cls):
print(f"Company: {cls.company}")
Employee.display_company()
2
3
4
5
6
7
8
9
10
#静态方法
class Math:
@staticmethod # 表明函数“add”是静态方法,不接受“self”和“cls”,基本上就是普通函数
def add(x, y):
return x + y
Math.add(3, 5)
2
3
4
5
6
7
8
多线程
多线程类似于同时执行多个不同程序,使用线程可以把占据长时间的程序中的任务放到后台去处理、一定程度上提高程序运行速度 线程是一个相对而言高级的概念,本例简单展示,实际过程中需要考虑线程同步等问题
- 创建线程:
threading.Thread(target=function,[args=,kwargs=])
,一般使用上述函数建立一个新线程,需要传入function
(线程中运行的函数)、args
(可选参数,必须是 tuple 类型)、kwargs
(可选参数,必须是字典类型) - 开始线程:
start()
- 结束线程:
join()
,主线程将等待该线程结束
import threading # 导入线程模块
import time # 用于sleep,体现线程的并行
def print_nums(threadname, size):
for i in range(size):
print(f"this is {threadname}", i)
time.sleep(i) # 线程之间的打印可以错开,来展现并行
thread1 = threading.Thread(target=print_nums, args=("thread1", 3))
thread2 = threading.Thread(target=print_nums, args=("thread2", 4))
thread1.start() # 几乎同时启动两个线程
thread2.start()
thread1.join() # 等待两个线程结束
thread2.join()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18