笔记:Python 语法要点汇总
Python 解释器
交互模式
脚本模式
源码编码
# -*- coding:utf-8 -*-
注释,#注释 及"""注释"""数据结构
标准数据类型
ython3 中有六个标准的数据类型:
- Number(数字)
- String(字符串)
- List(列表)
- Tuple(元组)
- Set(集合)
- Dictionary(字典)
Python3 的六个标准数据类型中:
- 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
- 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
数字(Number)
_
字符串(String)
\
可以用来转义引号r ,可获取原始字符串
字符串文本能够分成多行,使用三引号:"""..."""
或者 '''...''',
行尾换行符会被自动包含到字符串中,可以在行尾加上 \
来避免这个行为
字符串可以由 +
操作符连接(粘到一起),可以由 *
表示重复
s[:i] + s[i:]
永远等于 s
切片的索引有非常有用的默认值;省略的第一个索引默认为零,省略的第二个索引默认为切片的字符串的大小
+---+---+---+---+---+---+ | P | y | t | h | o | n | +---+---+---+---+---+---+ 0 1 2 3 4 5 6-6 -5 -4 -3 -2 -1对于非负索引,如果上下都在边界内,切片长度就是两个索引之差
\n
在单引号('...'
)和双引号("..."
)中具有相同的含义。"
(但你必须转义 \'
)列表(List)
append(x)
list.
insert
(i, x)list.
remove
(x);如果没有这样的元素,就会返回一个错误list.
pop
([i]);如果没有指定索引,a.pop()
返回最后一个元素list.
clear
()list.
index
(x);如果没有匹配的元素就会返回一个错误list.
count
(x);list.
sort
()list.
reverse
()list.
copy
();等同于 a[:]
list.
extend(L)
把列表当作堆栈使用
append()
方法可以把一个元素添加到堆栈顶。用不指定索引的 pop()
方法可以把一个元素从堆栈顶释放出来把列表当作队列使用
列表推导式
squares x ()squares.append(x)注意这个 for 循环中的被创建(或被重写)的名为
x
的变量在循环完毕后依然存在squares xx ()列表推导式可使用复杂的表达式和嵌套函数
嵌套的列表推导式
del语句
元组
集合
set()
而不是 {},后者用于创建空字典
集合推导式语法,a = {x for x in 'abracadabra' if x not in 'abc'}
字典
{}
del
来删除键:值对(key:value)list(d.keys())
将返回一个字典中所有关键字组成的无序列表sorted(d.keys())
dict(sape=4139, guido=4127, jack=4098)
循环技巧
items()
方法同时解读出来:knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():print(k, v)
for i, v in enumerate(['tic', 'tac', 'toe']):print(i, v)
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):print('What is your {0}? It is {1}.'.format(q, a))
for i in reversed(range(1, 10, 2)):print(i)
basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
for f in sorted(set(basket)):print(f)
words = ['cat', 'window', 'defenestrate']
for w in words[:]: # Loop over a slice copy of the entire list.
if len(w) > 6:
words.insert(0, w)
深入条件控制
while
和 if
语句中使用的条件不仅可以使用比较,而且可以包含任意的操作in
和 not in
审核值是否在一个区间之内is
和 is not
比较两个对象是否相同a < b == c
审核是否 a
小于 b
并且 b
等于 c
。and
和 or
组合,比较的结果可以用 not
来取反义and
和 or
也称作短路操作符,它们的参数从左向右解析,一旦结果可以确定就停止A
和 C
为真而 B
为假, A and B and C
不会解析 C
比较序列和其它类型
<
或者 >
比较的对象只要具有合适的比较方法就是合法的控制流程
if 语句
for 语句
range() 函数
for i in range(5):print(i)
range(10)
生成了一个包含 10 个值的链表,它用链表的索引值填充了这个长度为 10 的列表,所生成的链表中不包括范围中的结束值。也可以让 range() 操作从另一个数值开始,或者可以指定一个不同的步进值(甚至是负数,有时这也被称为 “步长”):range(5, 10) 5 through 9range(0, 10, 3) 0, 3, 6, 9range(-10, -100, -30) -10, -40, -70
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):print(i, a[i])
输出
0 Mary1 had2 a3 little4 lamb不过,这种场合可以方便的使用 enumerate()
在不同方面 range() 函数返回的对象表现为它是一个列表,但事实上它并不是。当你迭代它时,它是一个能够像期望的序列返回连续项的对象;但为了节省空间,它并不真正构造列表。
我们称此类对象是 可迭代的,即适合作为那些期望从某些东西中获得连续项直到结束的函数或结构的一个目标(参数)。我们已经见过的 for 语句就是这样一个迭代器。list() 函数是另外一个( 迭代器 ),它从可迭代(对象)中创建列表
break 和 continue 语句, 以及循环中的 else 子句
else
子句与 try 语句的 else
子句比与 if 语句的具有更多的共同点:try 语句的 else
子句在未出现异常时运行,循环的 else
子句在未出现 break
时运行。更多关于 try 语句和异常的内容,请参见 异常处理。pass 语句
class MyEmptyClass:pass
def initlog(*args):pass # Remember to implement this!
定义函数
None
result.append(b)
称为链表对象 result
的一个 方法。方法是一个“属于”某个对象的函数,它被命名为 obj.methodename
,这里的 obj
是某个对象(可能是一个表达式), methodename
是某个在该对象类型定义中的方法的命名深入 Python 函数定义
默认参数值
1 2 3 4 5 6 7 8 9 10 11 | def ask_ok(prompt, retries = 4 , complaint = 'Yes or no, please!' ): while True : ok = input (prompt) if ok in ( 'y' , 'ye' , 'yes' ): return True if ok in ( 'n' , 'no' , 'nop' , 'nope' ): return False retries = retries - 1 if retries < 0 : raise OSError( 'uncooperative user' ) print (complaint) |
这个函数可以通过几种不同的方式调用:
- 只给出必要的参数:
ask_ok('Do you really want to quit?')
- 给出一个可选的参数:
ask_ok('OK to overwrite the file?', 2)
- 或者给出所有的参数:
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
这个例子还介绍了 in 关键字。它测定序列中是否包含某个确定的值。
关键字参数
函数可以通过 关键字参数 的形式来调用,形如keyword = value
在函数调用中,关键字的参数必须跟随在位置参数的后面。传递的所有关键字参数必须与函数接受的某个参数相匹配 (例如
actor
不是 parrot
函数的有效参数),它们的顺序并不重要可变参数列表
可变
参数是参数列表中的最后一个,因为它们将把所有的剩余输入参数传递给函数。任何出现在 *args
后的参数是关键字参数,这意味着,他们只能被用作关键字,而不是位置参数:参数列表的分拆
*
操作符来自动把参数列表拆开**
操作符分拆关键字参数为字典Lambda 形式
lambda a, b: a+b
。 Lambda 形式可以用于任何需要的函数对象。出于语法限制,它们只能有一个单独的表达式。语义上讲,它们只是普通函数定义中的一个语法技巧。类似于嵌套函数定义,lambda 形式可以从外部作用域引用变量文档字符串
1 2 3 4 5 6 7 8 | def my_function(): """Do nothing, but document it. No, really, it doesn't do anything. """ pass print (my_function.__doc__) |
函数注解
__annotations__
属性中,对函数的其它部分没有任何影响。参数注解(Parameter annotations)是定义在参数名称的冒号后面,紧随着一个用来表示注解的值得表达式。返回注释(Return annotations)是定义在一个 ->
后面,紧随着一个表达式,在冒号与 ->
之间编码风格
- 使用 4 空格缩进,而非 TAB
在小缩进(可以嵌套更深)和大缩进(更易读)之间,4空格是一个很好的折中。TAB 引发了一些混乱,最好弃用
- 折行以确保其不会超过 79 个字符
这有助于小显示器用户阅读,也可以让大显示器能并排显示几个代码文件
- 使用空行分隔函数和类,以及函数中的大块代码
- 可能的话,注释独占一行
- 使用文档字符串
- 把空格放到操作符两边,以及逗号后面,但是括号里侧不加空格:
a = f(1, 2) + g(3, 4)
- 统一函数和类命名
推荐类名用
驼峰命名
, 函数和方法名用小写_和_下划线
。总是用self
作为方法的第一个参数(关于类和方法的知识详见 初识类 ) - 不要使用花哨的编码,如果你的代码的目的是要在国际化环境。Python 的默认情况下,UTF-8,甚至普通的 ASCII 总是工作的最好
- 同样,也不要使用非 ASCII 字符的标识符,除非是不同语种的会阅读或者维护代码
模块
.py
后缀__name__
得到fib = fibo.fib
print(fib(500))
深入模块
modname.itemname
这样不会从局域语义表中导入模块名from fibo import fib, fib2
fib(500)
_
)开头的命名from fibo import *需要注意的是在实践中往往不鼓励从一个模块或包中使用
*
导入所有,因为这样会让代码变得很难读import imp; imp.reload(modulename)
。作为脚本来执行模块
模块中的代码会被执行,就像导入它一样,不过此时python fibo.py <arguments>
__name__
被设置为 "__main__"
。这相当于,如果你在模块后加入如下代码if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
模块的搜索路径
spam
的模块时,解释器先在当前目录中搜索名为 spam.py
的文件。如果没有找到的话,接着会到 sys.path 变量中给出的目录列表中查找。 sys.path 变量的初始值来自如下:- 输入脚本的目录(当前目录)。
- 环境变量 PYTHONPATH 表示的目录列表中搜索
(这和 shell 变量
PATH
具有一样的语法,即一系列目录名的列表)。 - Python 默认安装路径中搜索
“编译的” Python 文件
__pycache__
目录下以 module.version.pyc
名字缓存每个模块编译后的版本,这里的版本编制了编译后文件的格式。它通常会包含 Python 的版本号- 为了减少一个编译模块的大小,你可以在 Python 命令行中使用 -O 或者 -OO。-O 参数删除了断言语句,-OO 参数删除了断言语句和 __doc__ 字符串。
因为某些程序依赖于这些变量的可用性,你应该只在确定无误的场合使用这一选项。“优化的” 模块有一个 .pyo 后缀而不是 .pyc 后缀。未来的版本可能会改变优化的效果。
- 来自
.pyc
文件或.pyo
文件中的程序不会比来自.py
文件的运行更快;.pyc
或.pyo
文件只是在它们加载的时候更快一些。 - compileall 模块可以为指定目录中的所有模块创建
.pyc
文件(或者使用 -O 参数创建.pyo
文件)。 - 在 PEP 3147 中有很多关这一部分内容的细节,并且包含了一个决策流程。
标准模块
sys.path
是解释器模块搜索路径的字符串列表dir() 函数
包
sys.path
搜索路径查找包含这个包的子目录import sound.effects.echo
这样就导入了 sound.effects.echo
子模块,必需通过完整的名称来引用sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
from sound.effects import echo这样就加载了
echo
子模块,并且使得它在没有包前缀的情况下也可以使用,所以它可以如下方式调用:echo.echofilter(input, output, delay=0.7, atten=4)
from sound.effects.echo import echofilter这样就又一次加载了
echo
子模块,但这样就可以直接调用它的 echofilter()
函数:echofilter(input, output, delay=0.7, atten=4)
from package import item
方式导入包时,这个子项(item)既可以是包中的一个子模块(或一个子包),也可以是包中定义的其它命名,像函数、类或变量import item.subitem.subsubitem
这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是前面子项中定义的类、函数或变量从 * 导入包
from package import *
时,如果包中的 __init__.py
代码定义了一个名为 __all__
的列表,就会按照列表中给出的模块名进行导入__all__ = ["echo", "surround", "reverse"]这意味着
from sound.effects import *
语句会从 sound
包中导入以上三个已命名的子模块__all__
, from sound.effects import *
语句 不会 从 sound.effects
包中导入所有的子模块。无论包中定义多少命名,只能确定的是导入了 sound.effects
包(可能会运行 __init__.py
中的初始化代码)以及包中定义的所有命名会随之导入。import *
时它只导出符合某种规范/模式的命名,仍然不建议在生产代码中使用这种写法from Package import specific_submodule
没有错误!事实上,除非导入的模块需要使用其它包中的同名子模块,否则这是推荐的写法包内引用
sound
包),可以按绝对位置从相邻的包中引入子模块。例如,如果 sound.filters.vocoder
包需要使用 sound.effects
包中的 echo
模块,它可以 from sound.Effects import echo
from module import name
来写显式的相对位置导入。那些显式相对导入用点号标明关联导入当前和上级包"__main__"
,Python 应用程序的主模块应该总是用绝对导入多重目录中的包
__init__.py
文件代码执行之前,该变量初始化一个目录名列表。该变量可以修改,它作用于包中的子包和模块的搜索功能输入和输出
格式化输出
print('We are the {} who say "{}!"'.format('knights', 'Ni'))
print('This {food} is {adjective}.'.format(food='spam', adjective='absolutely horrible'))
import math输出
print('The value of PI is approximately {}.'.format(math.pi))
print('The value of PI is approximately {!r}.'.format(math.pi))
':'
后面加一个整数会限定该字段的最小宽度,这在美化表格时很有用table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}输出
for name, phone in table.items():
print('{0:10} ==> {1:10d}'.format(name, phone))
旧式的字符串格式化
%
也可以用于字符串格式化print('The value of PI is approximately %5.3f.' % math.pi)
文件读写
f = open('workfile', 'w')
错误和异常
语法错误
异常
异常处理
while True:
try:
x = int(input("Please enter a number: "))
break
except ValueError:
print("Oops! That was no valid number. Try again...")
try 语句按如下方式工作。
- 首先,执行 try 子句 (在 try 和 except 关键字之间的部分)。
- 如果没有异常发生, except 子句 在 try 语句执行完毕后就被忽略了。
- 如果在 try 子句执行过程中发生了异常,那么该子句其余的部分就会被忽略。
如果异常匹配于 except 关键字后面指定的异常类型,就执行对应的except子句。然后继续执行 try 语句之后的代码。
- 如果发生了一个异常,在 except 子句中没有与之匹配的分支,它就会传递到上一级 try 语句中。
如果最终仍找不到对应的处理语句,它就成为一个 未处理异常,终止程序运行,显示提示信息。
instance.args
的参数中抛出异常
用户自定义异常
定义清理行为
预定义清理行为
类
Python 作用域和命名空间
z.real
中的 real
是对象 z
的一个属性modname.funcname
中,modname
是一个模块对象,funcname
是它的一个属性del modname.the_answer
会从 modname
对象中删除 the_answer
属性类定义语法
class ClassName:
<statement-1>
.
.
.
<statement-N>
类对象
obj.name
。类对象创建后,类命名空间中所有的命名都是有效属性名__doc__
也是一个有效的属性,返回类的文档字符串x = MyClass()以上创建了一个新的类 实例 并将该对象赋给局部变量
x
__init__()
的特殊方法def __init__(self):
self.data = []
__init__()
方法的话,类的实例化操作会自动为新创建的类实例调用 __init__()
方法__init__()
方法可以有参数,参数通过 __init__()
传递到类的实例化操作上class Complex:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x = Complex(3.0, -4.5)
x.r, x.i
实例对象
方法对象
类和实例变量
一些说明
self
。这仅仅是一个约定:对 Python 而言,名称 self
绝对没有任何特殊含义函数定义代码不一定非得定义在类中:也可以将一个函数对象赋值给类中的一个局部变量
方法可以像引用普通的函数那样引用全局命名。与方法关联的全局作用域是包含类定义的模块。
每个值都是一个对象,因此每个值都有一个 类( class ) (也称为它的 类型( type ) ),它存储为
object.__class__
继承
class DerivedClassName(BaseClassName):命名
<statement-1>
.
.
.
<statement-N>
BaseClassName
(示例中的基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用class DerivedClassName(modname.BaseClassName):构造派生类对象时,就记住了基类
- 函数 isinstance() 用于检查实例类型:
isinstance(obj, int)
只有在obj.__class__
是 int 或其它从 int 继承的类型 - 函数 issubclass() 用于检查类继承:
issubclass(bool, int)
为True
,因为 bool 是 int 的子类。
多继承
私有变量
_spam
)会被处理为 API 的非公开部分(无论它是一个函数、方法或数据成员)。它会被视为一个实现细节,无需公开__spam
的标识(前面至少两个下划线,后面至多一个),被替代为 _classname__spam
,去掉前导下划线的 classname
即当前的类名。此语法不关注标识的位置,只要求在类定义内class Mapping:需要注意的是编码规则设计为尽可能的避免冲突,被认作为私有的变量仍然有可能被访问或修改
def __init__(self, iterable):
self.items_list = []
self.__update(interable)
def update(self, interable):
for item in interable:
self.items_list.append(item)
__update = update
class MappingSubclass(Mapping):
def update(self, keys, values):
for item in zip(keys, values):
self.items_list.append(item)
exec()
, eval()
时不考虑所调用的类的类名,视其为当前类,这类似于 global
语句的效应,已经按字节编译的部分也有同样的限制。这也同样作用于 getattr()
, setattr()
和 delattr()
,像直接引用 __dict__
一样。补充
class Employee:实例方法对象也有属性:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
m.__self__
是一个实例方法所属的对象,而 m.__func__
是这个方法对应的函数对象异常也是类
raise Class
raise Instance
Class
必须是 type 或其派生类的一个实例。第二种形式是以下形式的简写raise Class()
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except D:
print("D")
except C:
print("C")
except B:
print("B")
迭代器
self
:class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
生成器
def reverse(data):基于类的迭代器,它能作的每一件事生成器也能作到
for index in range(len(data)-1, -1, -1):
yield data[index]
for char in reverse('golf'):
print(char)
生成器表达式
__dict__
,它返回用于实现模块命名空间的字典,命名 __dict__
是一个属性而非全局命名。显然,使用它违反了命名空间实现的抽象原则,应该被严格限制于调试中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律