Python-基础

Python 学习笔记

Python3 官方中文文档 https://docs.python.org/zh-cn/3.13/

《Python进阶》 -- 《Intermediate Python》的中文译本 https://eastlakeside.gitbook.io/interpy-zh

Python 的练手项目有哪些值得推荐? https://www.zhihu.com/question/29372574

《Python Cookbook》3rd Edition - 中文翻译 https://python3-cookbook.readthedocs.io/zh_CN/latest/copyright.html


__init__.py

Python package 中的 __init__.py 相当于包本身的代码,在包被 import 时会执行 __init__.py 中的代码。 Python3 的包中可以没有 __init__.py,但 Python2 的包中必须有否则会报错。

__all__ 默认导入模块列表

__all__ 是一个特殊的列表变量,用于声明模块中哪些成员(函数、类、变量等)应该被公开导出。 当用户使用 from module import * 引入包时,​​只有被列入 __all__ 的成员才会被导入​​。 __all__ 用于显式控制模块的公开接口。

通常在包的 __init__.py 文件中定义 __all__: 例如有目录结构:

my_package/
    ├── __init__.py
    ├── module1.py
    └── module2.py

__init__.py 中:

# my_package/__init__.py

# 显式导入子模块
from .module1 import func1, ClassA
from .module2 import func2

# 定义允许导出的成员
__all__ = ['func1', 'ClassA', 'func2']

__pycache__ 文件夹

__pycache__ 文件夹在 Python 项目中用于存储模块的​​编译后字节码缓存​​(.pyc 文件),以提高程序运行效率。

Python 在首次导入模块(如 import my_module)时,会将 .py 源码编译成​​字节码​​(.pyc 文件),并缓存在 __pycache__ 中。后续导入时直接读取缓存,跳过编译步骤,加快启动速度。

​自动生成​​ 仅当模块被​​导入​​时生成。直接运行的脚本(如 python main.py)不会为其生成 .pyc,除非它也被其他模块导入。

​​无需手动管理​​ Python 自动处理缓存的创建和更新。删除 __pycache__ 不会影响程序功能,但下次导入时会重新生成。


主文件

__name__ 属性

在 Python 中,每个模块都有一个内置的属性 __name__

  • 当直接运行一个 py 文件时,这个属性的值是 __main__,或者说,当一个 py 文件直接被 Python 解释器执行时,内部 __name__ = '__main__'
  • 当一个模块被导入到其他模块中时,__name__ 的值则是该模块的名称,或者说,当一个 py 文件被导入到其他 py 文件中执行时,__name__ = '模块名'

所以通过 __name__ 是否等于 '__main__' 可以区分当前 py 文件是否被直接执行

所以 Python 入口文件中一般都有一行:

if __name__ == '__main__':
  print("项目入口")

但一些非项目入口 py 中也有 if __name == '__main__':,这样一般是为了方便单独调试此功能模块。


import

ImportError: No module named xxx

问题: 在 PyCharm 中可运行,在命令行中 python xx.py 运行报错 ImportError: No module named xxx,报错是自定义模块

原因: 在 IDE 中执行 python 程序,编译器会自动把当前项目的根目录加入到包查找路径中,可以理解为加到 PYTHONPATH 下,所以直接执行是没有问题的。 但是在 cmd 或者 terminal 控制台中直接使用 python 命令来执行程序,不会自动将当前项目加入到 PYTHONPATH 环境变量下,如果涉及到 import 其他文件夹下的变量就会报类似ImportError: No module named xxx 这样的错误。

解决: 使用 sys.path.append() 命令把报警包的所在文件夹路径加入到 sys.path

import 相对路径和绝对路径

https://python3-cookbook.readthedocs.io/zh-cn/latest/c10/p03_import_submodules_by_relative_names.html

Python 包搜索目录(sys.path)

Python 的导入顺序主要依赖于 sys.path 中的路径顺序。当导入一个模块时,Python 会按照 sys.path 列表中的目录顺序依次查找,一旦找到匹配的模块或包,就会停止搜索。 导入一个叫 mod1 的模块时,解释器先在当前目录中搜索名为 mod1.py 的文件。如果没有找到的话,接着会到 sys.path 变量中给出的目录列表中查找。

sys.path 变量的初始值来自如下:

  • 输入脚本的目录(当前目录)。
  • 环境变量 PYTHONPATH 表示的目录列表中搜索(这和 shell 变量 PATH 具有一样的语法,即一系列目录名的列表)。
  • Python 默认安装路径中搜索。

解释器由 sys.path 变量指定的路径目录搜索模块,该变量初始化时默认包含了输入脚本(或者当前目录), PYTHONPATH 和安装目录。这样就允许 Python 程序了解如何修改或替换模块搜索目录。

sys.path 搜索路径顺序

项目目录中有和标准库同名的模块时,会优先加载项目目录中的模块,因为 sys.path 的第一个路径通常是当前运行的脚本所在的目录,排在标准库路径之前。 Python的模块搜索顺序由 sys.path 列表的路径顺序决定,具体优先级为:

  • 内建模块(如 sys, os 等)
  • 当前运行脚本的目录(即项目的根目录)
  • PYTHONPATH 环境变量中的路径
  • 标准库目录
  • 第三方库目录(如 site-packages)

避免同名包或文件名污染Python标准库

曾经遇到的问题,项目中有个名字是 socket 的目录(包),导致 PyCharm 中 debug 代码时都会报错:

    def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'socket' has no attribute '_GLOBAL_DEFAULT_TIMEOUT'

原因: 当 Python 遇到 import socket 语句时,它会按以下顺序查找:

  • 当前目录
  • PYTHONPATH 环境变量中的目录
  • 标准库目录
  • 已安装的第三方包目录

由于项目中有一个 socket 目录,Python 在第一步就找到了它,而不会继续查找标准库中的 socket 模块。 这导致 PyCharm 的调试器无法正确导入标准库的 socket 模块,从而找不到 _GLOBAL_DEFAULT_TIMEOUT 属性。


typing 类型标注(3.5+)

https://docs.python.org/zh-cn/3.7/library/typing.html

typing 类型标注是 3.5 新版功能,用于在代码中显式声明变量、函数参数和返回值的类型,是提高代码可读性、维护性和可靠性的核心工具。 Python 运行时并不强制标注函数和变量类型。类型标注可被用于第三方工具,比如类型检查器、集成开发环境、静态检查器等。

例如,下面函数接受并返回一个字符串,greeting 函数的参数 name 预期是 str 类型,并且返回 str 类型。

def greeting(name: str) -> str:
    return 'Hello ' + name

变量标注

# 变量标注
name: str = "Alice"
age: int = 30
height: float = 1.75
is_active: bool = True

函数标注

函数 add 接受两个 int 类型的参数,第二个参数默认值是0,返回值也是 int 类型

def add(a: int, b: int = 0) -> int:
    return a + b

容器泛型标注

使用 typing 模块中的泛型(如 List、Dict)

from typing import List, Tuple, Dict, Set

numbers: List[int] = [1, 2, 3]  # 列表
coordinates: Tuple[float, float] = (3.14, -2.71)  # 元组
ages: Dict[str, int] = {"Alice": 30, "Bob": 25}  # 字典
unique_names: Set[str] = {"Alice", "Bob", "Charlie"}  # 集合

# 嵌套列表
matrix: List[List[int]] = [[1, 2], [3, 4]]

# 嵌套字典
students: Dict[str, Dict[str, int]] = {
    "Alice": {"math": 90, "english": 85},
    "Bob": {"math": 80, "english": 75}
}

def process(data: List[int]) -> Tuple[str, int]:
    return ("result", len(data))

从 Python 3.9 开始,可以直接使用内置的容器类型进行标注,例如 例如 list[int] 代替 List[int]

def process(data: list[int]) -> tuple[str, int]:
    return ("result", len(data))

Union 可能是多类型之一

Union: 表示多种类型中的一种。例如 Union[int, str] 表示可以是整数或字符串。

from typing import Union

def process_value(value: Union[int, float, str]) -> Union[int, float]:
    # 处理不同类型的值
    pass

3.10+ 中,可以使用 | 代替 Union Optional[Union[int, str]] 可以简写成 int | str | None


Optional 类型或 None

Optional[T] 表示一个值可以是类型 T 或者 None Optional[T] 等价于 Union[T, None]

from typing import Union, Optional

# 函数返回值是 str 或 None
def parse(value: Union[int, str]) -> Optional[str]:
    return str(value) if value else None

Any 任意类型

https://docs.python.org/zh-cn/3.7/library/typing.html#the-any-type

Any 是一种特殊的类型。静态类型检查器将所有类型视为与 Any 兼容,反之亦然,Any 与所有类型相兼容,相当于没有类型检查。

下面 foo 函数的参数 item 可以是任何类型,可能包含一个 bar 函数

def foo(item: Any) -> int:
    # Typechecks; 'item' could be any type, and that type might have a 'bar' method
    item.bar()
    ...

TypeVar 泛型

TypeVar 用于定义泛型函数或类。泛型可以在保持类型安全的同时,提供一定的灵活性。

from typing import TypeVar, List

T = TypeVar('T')  # 声明泛型类型变量

def first(items: List[T]) -> T:
    return items[0]

# 调用时自动推断类型
result: int = first([1, 2, 3])  # 推断出 T 是 int

TypedDict 结构化字典(3.8+)

为字典键提供类型约束

from typing import TypedDict

class User(TypedDict):
    name: str
    age: int
    is_admin: bool = False  # 可选默认值

user: User = {"name": "Bob", "age": 30}

Callable 可调用对象

Callable 用于标注函数类型,格式为 Callable[[参数类型], 返回类型]

from typing import Callable

def apply_function(func: Callable[[int, int], int], a: int, b: int) -> int:
    return func(a, b)

add = lambda x, y: x + y
result = apply_function(add, 3, 5)

Iterable/Iterator 可迭代对象

from typing import Iterable, Iterator

def process_items(items: Iterable[str]) -> None:
    for item in items:
        print(item)

def count_down(n: int) -> Iterator[int]:
    while n > 0:
        yield n
        n -= 1

Literal 指定常量

Literal 用于表示一个变量必须是特定的值(而不是类型)。这在函数参数限制为几个可能的值时很有用。

from typing import Literal

def set_status(status: Literal["active", "inactive", "pending"]) -> None:
    pass

def draw_shape(shape: Literal["circle", "square", "triangle"]):
    print(f"Drawing a {shape}")

draw_shape("circle")  # 正确
draw_shape("oval")    # 静态类型检查会报错

Final 不可变量

表示变量不应被重新赋值:

from typing import Final

MAX_CONNECTIONS: Final[int] = 10

类型别名

可以给类型定义别名

例如下面 ConnectionOptions 类型就等于 Dict[str, str] 类型,可用于简化复杂类型签名:

from typing import Dict, Tuple, Sequence

ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: Sequence[Server]) -> None:
    ...

# 等价于
def broadcast_message(
        message: str,
        servers: Sequence[Tuple[Tuple[str, int], Dict[str, str]]]) -> None:
    ...

命令行参数

sys.argv 命令行参数

可以通过 sys 包的 sys.argv 来获取简单的命令行参数 sys.argv 是命令行参数列表。 len(sys.argv) 是命令行参数个数。 sys.argv[0] 表示脚本名。

from sys import argv

print('当前脚本名称:', argv[0])
print('参数个数:', len(argv))
print('参数列表:', argv)

执行: python base/test_argv.py 11 22 3 输出:

当前脚本名称: base/test_argv.py
参数个数: 4
参数列表: ['base/test_argv.py', '11', '22', '3']

argparse 高级命令行参数解析

argparse 是 Python 标准库中的一个模块,用于编写用户友好的命令行接口。它允许程序从命令行解析参数和选项。

使用步骤:

  1. 导入模块:import argparse
  2. 创建解析器:parser = argparse.ArgumentParser(description='描述脚本用途')
  3. 添加参数:parser.add_argument()
  4. 解析参数:args = parser.parse_args()
  5. 使用参数:通过 args.参数名 访问参数值。

参数类型: 1、位置参数 (Positional Arguments) 位置参数是必填的,按顺序依次赋值给参数名,位置参数必须定义在可选参数之前(逻辑上更清晰)。

parser.add_argument("filename", help="输入文件名")

2、可选参数 (Optional Arguments) 以 --- 开头的是可选参数,如 --output

parser.add_argument('-P', default=3306, type=int, help='端口')

参数属性: type 指定参数类型(如 int, float, str)。 parser.add_argument("-n", type=int, help="输入一个整数")

default 设置默认值 rser.add_argument("--size", default=10)

choices 指定可选值 parser.add_argument("--color", choices=["red", "green", "blue"])

required 强制要求提供参数(仅对可选参数有效)。 parser.add_argument("--input", required=True)

nargs 指定参数个数:? 0或1个,* 0或多个,+ 1个或多个 parser.add_argument("files", nargs="+", help="一个或多个文件")

action action 参数用于定义命令行参数的行为逻辑,控制参数值的存储方式或触发特定操作。

  • store 默认动作,​存储参数值(用户必须显式提供值)。 例如 parser.add_argument("--file", action="store", type=str) 用户输入 --file data.txt 时,args.file 将被赋值为 "data.txt"
  • store_true / store_false 将参数视为布尔开关,无需额外值。 store_true:参数存在时存储 True,否则 False。 store_false:参数存在时存储 False,否则 True。 例如 parser.add_argument("-v", "--verbose", action="store_true") 用户输入 -v 时,args.verbose 变为 True
  • store_const 存储预定义的常量值(通过 const 参数指定)。 例如 parser.add_argument("--mode", action="store_const", const="debug") 输入 --mode 时,args.mode 被赋值为 "debug"
  • append 将多次出现的参数值追加到列表。 例如 parser.add_argument("--tag", action="append") 输入 --tag A --tag B 时,args.tag 变为 ["A", "B"]
  • count 统计参数出现的次数。 例如 parser.add_argument("-v", "--verbose", action="count", default=0) 输入 -vvv 时,args.verbose 为 3

示例

import argparse

parser = argparse.ArgumentParser(description='Python命令行参数解析示例',
                                 epilog='示例用法:python base/test_argparse.py pp1 pp2 -u root -P 3306 -p 111 --files f1 f2 f3',
                                 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('pos1', help='pos1参数')
parser.add_argument('pos2', help='pos2参数')
parser.add_argument('-H', '--host', default='localhost', type=str, help='server地址')
parser.add_argument('-P', default=3306, type=int, help='端口')
parser.add_argument('-u', default='root', type=str, help='用户')
parser.add_argument('-p', type=str, required=True, help='密码,必填值')
parser.add_argument('--color', choices=['red', 'green'], help='颜色')
parser.add_argument('--files', nargs="+", help='一个或多个文件')

args = parser.parse_args()

print('参数:', args)
print('pos1: ', args.pos1)
print('pos2: ', args.pos2)
print('host: ', args.host)
print('files: ', args.files)

帮助信息:

python base/test_argparse.py -h                                              
usage: test_argparse.py [-h] [-H HOST] [-P P] [-u U] -p P [--color {red,green}] [--files FILES [FILES ...]] pos1 pos2

Python命令行参数解析示例

positional arguments:
  pos1                  pos1参数
  pos2                  pos2参数

options:
  -h, --help            show this help message and exit
  -H HOST, --host HOST  server地址 (default: localhost)
  -P P                  端口 (default: 3306)
  -u U                  用户 (default: root)
  -p P                  密码,必填值 (default: None)
  --color {red,green}   颜色 (default: None)
  --files FILES [FILES ...]
                        一个或多个文件 (default: None)

示例用法:python base/test_argparse.py pp1 pp2 -u root -P 3306 -p 111 --files f1 f2 f3

输出结果

python base/test_argparse.py pp1 pp2 -u root -P 3306 -p 111 --files f1 f2 f3
参数: Namespace(pos1='pp1', pos2='pp2', host='localhost', P=3306, u='root', p='111', color=None, files=['f1', 'f2', 'f3'])
pos1:  pp1
pos2:  pp2
host:  localhost
files:  ['f1', 'f2', 'f3']

Python 命令行参数的3种传入方式 https://tendcode.com/article/python-shell/

python之parser.add_argument()用法——命令行选项、参数和子命令解析器 https://blog.csdn.net/qq_34243930/article/details/106517985


作用域

Python 中无块作用域

Python 没有块级作用域(block scope),只有函数作用域、模块作用域和全局作用域。这意味着在 if, for, while 等代码块中定义的变量,在代码块外仍然可以访问。

if 内定义的变量在 if 外可见:

if True:
    x = 10  # 在 if 语句内定义变量 x
print(x)  # 在 if 外访问变量 x

__init__ 定义类的初始化方法

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

def __init__(self):
    pass

__iter__ 定义类的迭代方法

使得该类的对象可以被迭代

def __iter__(self): 
    return self 

__call__ 方法(对象当函数用)

Python 中 __call__ 方法是一个特殊的方法,它使得一个对象可以像函数一样被调用。

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

    def __call__(self, greeting):
        return f"{greeting}, {self.name}!"

# 创建一个CallableObject对象
hello = CallableObject("World")

# 调用这个对象
print(hello("Hello"))  # 输出:Hello, World!

这个例子中,CallableObject 类定义了一个 __call__ 方法,所以可以像函数一样调用 CallableObject 的对象。 当调用 hello("Hello") 时,Python 会自动调用 hello.__call__("Hello")


@staticmethod 静态方法装饰器

@staticmethod 是 Python 中用于声明‌静态方法‌的 装饰器

静态方法有如下特点:

  • 无需实例或类参数‌:静态方法不需要像实例方法那样传递 self 实例对象,也不用像类方法(@classmethod)那样传递 cls 类对象 作为第一个参数‌。
  • 通过类和实例调用‌:可直接通过类名调用,如 ClassName.static_method(),也可通过实例调用‌。
  • 不依赖类或实例属性‌:静态方法通常用于实现与类相关但无需访问类或实例属性的功能‌。

使用场景:一般将逻辑上与类相关但无需操作类/实例属性的函数封装为静态方法‌


@classmethod 类方法装饰器

@classmethod 是 Python 的核心装饰器之一,用于创建​​类方法​​。

核心特性:

  • cls 是第一个参数,指向类本身(类似实例方法中的 self 指向实例),增加 @classmethod 装饰器后无需手动传递 cls 参数(Python 自动处理)。
  • cls 参数在继承时自动绑定到当前子类,支持在继承链中实现多态行为
  • 可通过​​类名​​调用:ClassName.method(),也可通过​​实例​​调用(不推荐):instance.method()

例1,操作类级别的属性

class Settings:
    _theme = "light"

    @classmethod
    def get_theme(cls):
        return cls._theme

    @classmethod
    def set_dark_mode(cls):
        cls._theme = "dark"

# 调用类方法修改类状态
Settings.set_dark_mode()
print(Settings.get_theme())  # 输出: dark

例2,实现多态继承

class Shape:
    @classmethod
    def create(cls, sides):
        return cls(sides)

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

class Triangle(Shape):
    # 继承create方法但返回Triangle实例
    def area(self):
        print(f"计算{sides}边形面积")

# 创建子类实例
t = Triangle.create(3)  # 返回 Triangle 实例

@dataclass 数据类

@dataclass 装饰器可简化数据模型定义,它能自动生成 __init__, __repr__, __eq__ 等方法,减少样板代码。

frozen=True 冻结实例,禁止修改字段值

@dataclass(frozen=True)
class Config:
    timeout: int = 10
    max_retries: int = 3

通过 metadata 添加验证逻辑:

@dataclass
class Product:
    id: int
    price: float = field(metadata={"validate": lambda v: v > 0})

可迭代对象

可迭代对象(Iteratable Object) 是能够一次返回其中一个成员的对象,如字符串、列表、元组、集合、字典等等之类的对象都属于可迭代对象 可迭代对象都有 __iter__ 方法,可通过 dir() 输出对象的全部属性、方法看到 isinstance(obj, Iterable) 判断对象是否可迭代对象:

>>> from collections import Iterable
>>> isinstance("str", Iterable)
True
>>> isinstance([1,2,3], Iterable)
True
>>> isinstance({"k": "v"}, Iterable)
True
>>> isinstance(35, Iterable)
False

迭代器

迭代器(Iterator) 是同时实现 __iter__()__next__() 方法的对象。 迭代器对象可通过 __next__() 方法或者一般的 for 循环进行遍历,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不能后退,终止迭代则会抛出 StopIteration 异常。

当迭代一个可迭代对象时,for 循环通过 iter() 方法获取要迭代的项,并使用 next() 方法返回后续的项。for 循环内部通过捕获 StopIteration 异常来判断迭代是否结束。

>>> iter="str".__iter__()  # 获取迭代器对象
>>> iter.__next__()
's'
>>> iter.__next__()
't'
>>> iter.__next__()
'r'
>>> iter.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

函数

https://docs.python.org/zh-cn/3.11/reference/compound_stmts.html#function-definitions https://docs.python.org/zh-cn/3.11/tutorial/controlflow.html#defining-functions

函数定义: def + 函数名标识符 + 圆括号(参数列表) + 英文冒号:,第二行缩进后开始编写函数内容

def func_name(arg1, arg2):
  print('func body')

pass 可用于定义空函数

def noOps():
  pass

Python 函数都有返回值

函数在 return 语句后返回。

return 语句不带表达式参数时,返回 None return 等于 return None

如果没有 return 语句执行完后也会返回,此时返回的是 None 即使没有 return 语句的函数也会返回 None,即函数执行完毕退出也返回 None

下面的 printB 函数看起来只是输出 'B',其实也有返回值 None

def printB():
  pirnt('B')

DocString 文档字符串

函数内的第一条语句是字符串时,该字符串就是文档字符串,也称为 docstring 可以在函数体的第一行使用一对三个单引号 ''' 或者一对三个双引号 """ 来定义文档字符串。 可以使用 __doc__(注意双下划线)调用函数中的文档字符串属性。

例如

#!/usr/bin/python
# -*- coding: UTF-8 -*-

def function():
  ''' say something here!
  '''
  pass

print (function.__doc__) # 调用 doc

结果 say something here!


函数传参和返回是内存地址


函数参数的默认值

https://docs.python.org/zh-cn/3.11/tutorial/controlflow.html#default-argument-values

Python 函数参数默认值在函数定义时计算,且只计算一次

函数参数默认值在函数定义时计算 默认值在函数定义阶段被求值,生成的对象/值会绑定到函数对象上 例如,下面参数 arg 的默认值仅在函数定义时赋值为 i ,且赋值时 i 值是 5,后续函数对象 f 的参数 arg 的值就绑定了 5,i 的值再发生变化不会影响 arg 的值。

i = 5
def f(arg=i):
    print(arg)

i = 6
f()  # 输出5

函数参数的默认值只在定义时计算一次

列表等可变参数默认值的陷阱

默认值为列表、字典或类实例等可变对象时,会产生与该规则不同的结果。 后续调用共享同一个对象​​:如果默认值是可变的(如列表、字典等),每次调用函数时都会使用同一个对象

例如,下面的函数参数 L 默认值是空列表,仅在函数定义时计算一次,这个对象会被存储在函数的 __defaults__ 属性中,后续每次调用函数 f 如果不显示给 L 赋值,都会使用定义时初始化的列表,导致 L 中元素累积。

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

输出结果:

[1]
[1, 2]
[1, 2, 3]

不想在后续调用之间共享默认值时,应以如下方式编写函数:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

Python 中函数参数默认值是列表、集合、字典或类实例等可变对象时,默认值应在函数内部判断为 None 时主动赋值为空列表等


关键字参数与位置参数

https://docs.python.org/zh-cn/3.11/glossary.html#term-argument https://docs.python.org/zh-cn/3.11/tutorial/controlflow.html#keyword-arguments

在调用函数时传给 function (或 method )的值,参数分为两种:

关键字参数 在函数调用中前面带有标识符(例如 name=)或者作为包含在前面带有 ** 的字典里的值传入。 举例来说,3 和 5 在以下对 complex() 的调用中均属于关键字参数:

complex(real=3, imag=5)
complex(**{'real': 3, 'imag': 5})

位置参数 不属于关键字参数的参数。位置参数可出现于参数列表的开头,或者作为前面带有 * 的 iterable 里的元素被传入。 举例来说,3 和 5 在以下调用中均属于位置参数:

complex(3, 5)
complex(*(3, 5))

形参的几种形式

术语对照表 » parameter -- 形参 https://docs.python.org/zh-cn/3.11/glossary.html#term-parameter https://docs.python.org/zh-cn/3.11/tutorial/controlflow.html#special-parameters

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |             |                  |
        |        Positional or keyword   |
        |                                - Keyword only
         -- Positional only

位置或关键字参数(默认)

位置或关键字参数 positional-or-keyword,指定一个可以作为 位置参数 传入也可以作为 关键字参数 传入的实参。这是默认的形参类型。

例如下面的 foo 和 bar:

def func(foo, bar=None):
  pass

强制位置参数(/ 之前)

仅位置参数 positional-only,或强制位置参数,指定一个只能通过位置传入的参数。 / ​​左侧​​的参数​​只能通过位置传递​​(不能使用关键字)

PEP 570 – Python Positional-Only Parameters https://peps.python.org/pep-0570/

例1,下面的 posonly1 和 posonly2 都是仅位置参数

def func(posonly1, posonly2, /, positional_or_keyword):
  pass

例2,下面 a, b 是仅位置参数,不接受通过关键字传参,c 既可位置传参也可以关键字传参

def func(a, b, /, c):
    return a + b + c

# 正确调用
print(func(1, 2, 3))     # a=1, b=2, c=3
print(func(1, 2, c=3))   # a=1, b=2, c=3

# 错误调用
func(a=1, b=2, c=3)     # TypeError: a 和 b 是仅位置参数

强制关键字参数(**args 之后)

仅关键字参数 keyword-only,或强制关键字参数,指定一个只能通过关键字传入的参数。

PEP 3102 – Keyword-Only Arguments https://peps.python.org/pep-3102/

强制关键字参数:

  • * ​​右侧​​的参数​​是强制关键字参数,必须通过关键字传递​* 本身不捕获任何参数
  • *args 捕获零个或多个额外的位置参数,然后将后续参数强制为关键字参数。

例1,下面的 kw_only1 和 kw_only2 是强制关键字参数

def func(arg, *, kw_only1, kw_only2):
  pass

例2,下面 a 既可位置传参也可以关键字传参,b, c 是强制关键字参数,不能通过位置传参

def func(a, *, b, c):
    return a + b + c

# 正确调用
print(func(1, b=2, c=3))  # a=1, b=2, c=3

# 错误调用
func(1, 2, 3)            # TypeError: b 和 c 需要关键字传递

例3,a 仅位置,b 混合,c 是仅关键字参数,args/kwargs 捕获额外参数

def func(a, /, b, *args, c, **kwargs):
    pass

/* 组合使用​:

  • ​位置参数​​:在 / 左侧的是强制位置参数
  • ​混合参数​​:在 /* 之间的参数,既支持位置传参,也支持关键字传参
  • ​关键字参数​​:在 * 右侧的是强制关键字参数

例4,下面 a,b 是位置参数,d,e 是关键字参数,c 既支持位置传参,也支持关键字传参

def func(a, b, /, c, *, d, e):
    return a + b + c + d + e

# 正确调用
func(1, 2, 3, d=4, e=5)     # a=1, b=2, c=3(位置或关键字均可)
func(1, 2, c=3, d=4, e=5)   # c 可用关键字

# 错误调用
func(a=1, b=2, c=3, d=4, e=5)  # a, b 禁止关键字
func(1, 2, 3, 4, 5)            # d, e 必须用关键字

可变位置参数(*args)

可变位置参数 var-positional,即 元组可变参数,内部通过 元组 接收任意个位置参数 在参数名称前加缀 * 来定义可变位置参数

可变位置参数:

  • *args 会接收所有多余的位置参数
  • *args 必须在其他位置参数之后,因为 *args 后的参数会成为强制关键字参数
  • ​​*args 之后的所有参数都是强制关键字参数​​

例如,下面的 args 是可变位置参数,传参时匹配完第一个固定参数 arg1 后,剩余的参数以一元组的形式存储在 args 中

def function(arg1, *args):
  print arg1
  print len(args)

可变关键字参数(**kwargs)

可变关键字参数 var-keyword,指定可以提供任意数量的关键字参数(附加在其他形参已接受的关键字参数之后)。 也就是字典可变参数,可通过在形参名称前加缀 ** 来定义,内部用 字典 存储任意个关键字参数。

可变关键字参数:

  • **kwargs 会接收所有多余的关键字参数
  • **kwargs 必须放在其他关键字参数之后

例如,下面 a 是强制位置参数,b 任意,args 接收多余的位置参数,c 任意,kwargs 接收多余的关键字参数

def func(a, /, b, *args, c, **kwargs):
    print(f"a={a}, b={b}, args={args}, c={c}, kwargs={kwargs}")

func(1, 2, 3, 4, c=5, x=6, y=7)
# 输出: a=1, b=2, args=(3, 4), c=5, kwargs={'x':6, 'y':7}

特别的 def func(*args, **kwargs) 是最灵活的参数声明方式,可以捕获​​所有形式​​的参数传递:

  • *args 接收​​所有位置参数​​(打包成元组)
  • **kwargs 接收​​所有关键字参数​​(打包成字典)
  • 与普通参数一起使用时,必须按 普通位置参数 -> *args -> 命名关键字参数 -> **kwargs 的顺序排列,kwargs 必须是最后一个

传参示例:

def func(*args, **kwargs):
    print("位置参数:", args)
    print("关键字参数:", kwargs)
    print()

# 位置参数: (1, 2, 'hello'),关键字参数: {'name': 'Alice', 'age': 30}
func(1, 2, 'hello', name='Alice', age=30)

# 位置参数: (),关键字参数: {}
func()

# 位置参数: ('a', 'b'),关键字参数: {}
func('a', 'b')

# 位置参数: (),关键字参数: {'x': 10, 'y': 20}
func(x=10, y=20)

PEP 570 强制位置参数

PEP 570 – Python Positional-Only Parameters https://peps.python.org/pep-0570/

PEP 570 Positional-Only Parameters 是 Python 3.8 引入的核心语法特性,提出强制位置参数规范。

通过 / 符号明确划分参数传递规则,/ 左侧参数​​:​​仅限位置传递​​(禁止关键字参数)

例如,a b 是强制位置参数,不能通过关键字传参

def func(a, b, /, c): 
    return a + b + c

func(1, 2, 3)      # ✅ 正确:a=1, b=2, c=3  
func(1, 2, c=3)    # ✅ 正确:c 可关键字传递  
func(a=1, b=2, c=3) # ❌ 错误:a, b 禁用关键字  

PEP 3102 强制关键字参数

PEP 3102 – Keyword-Only Arguments https://peps.python.org/pep-3102/

​​PEP 3102​​ Keyword-Only Arguments 于 2006 年提出并在 ​​Python 3.0​​ 正式引入,提出强制关键字参数规范。

在函数参数列表中使用 * 作为分隔符,其​​右侧参数​​自动成为强制关键字参数

例如,kw1 kw2 是强制关键字参数,必须通过关键字传参

def func(a, b, *, kw1, kw2=None): 
    # a, b: 位置参数 | kw1, kw2: 强制关键字参数
    pass

内置函数

Python 标准库 » 内置函数 https://docs.python.org/zh-cn/3.11/library/functions.html

Python 的内置函数是用 C 语言实现的,无法直接查看源码。 在 PyCharm 中点击跳转的代码实现 builtins.py 是个类型存根文件(type stub file),用于提供类型提示(Type Hints)和代码补全,并非实际实现。这是 Python 类型系统的一部分(通过 typing 模块和 mypy 工具支持)。

range(n) 从0到n-1共n个整数

语法: range(stop) range(start, stop[, step]) 参数: start: 计数从 start 开始。默认是从 0 开始。例如 range(5) 等价于 range(0, 5) stop: 计数到 stop 结束,但不包括 stop。例如:range(0, 5) 是 [0, 1, 2, 3, 4] 没有5 step: 步长,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1)

注意: Python3 range() 函数返回的是一个可迭代对象(类型是对象),而不是列表类型。 Python3 list() 函数是对象迭代器,可以把 range() 返回的可迭代对象转为一个列表,返回的变量类型为列表。list(range(10)) 所以 print(range(3)) 不会直接打印列表,print(list(range(3))) 才打印列表。 Python2 range() 函数返回的是列表。

例如:

>>> print(*range(3))
0 1 2
>>> print(*range(0,3))
0 1 2
>>> print(*range(0,4,2))
0 2

可以使用负数作为步长,以便从结束值倒序生成序列。如果使用负数作为步长,则开始值必须大于结束值。 range(6, 1, -1) 结果 6 5 4 3 2


sorted() 排序

sorted(iterable, key=None, reverse=False) sorted(iterable, /, *, key=None, reverse=False) 根据 iterable 中的项返回一个新的已排序列表。

具有两个可选参数,它们都必须指定为关键字参数:

  • key 指定带有单个参数的函数,用于从 iterable 的每个元素中提取用于比较的键 (例如 key=str.lower)。 默认值为 None (直接比较元素)。
  • reverse 为一个布尔值。 如果设为 True,则每个列表元素将按反向顺序比较进行排序。

内置的 sorted() 确保是稳定的。 如果一个排序确保不会改变比较结果相等的元素的相对顺序就称其为稳定的。

>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

>>> sorted([5, 2, 3, 1, 4], reverse=True)
[5, 4, 3, 2, 1]

>>> sorted(['quick', 'brown', 'fox', 'jumps', 'over', 'lazy', 'dog'], key=len)
['fox', 'dog', 'over', 'lazy', 'quick', 'brown', 'jumps']

enumerate() 可迭代对象的(i,val)

enumerate(iterable, start=0) 用于在遍历可迭代对象时同时获取元素索引和值的内置函数。 它简化了传统循环中需要手动维护索引的复杂性,提升代码的简洁性和可读性。 参数: iterable 必需,表示可迭代对象(如列表、元组、字符串、字典等) start 可选,指定索引的起始值,默认为 0。例如,设置为 1 时,索引从 1 开始递增 返回值:一个枚举对象(迭代器),每次迭代返回 (index, value) 形式的元组

fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits):
    print(f"索引 {i}: {fruit}")

结果: 索引 0: apple 索引 1: banana 索引 2: cherry


zip() 并行迭代

https://docs.python.org/zh-cn/3.11/library/functions.html#zip zip(*iterables, strict=False) 在多个迭代器上并行迭代,从每个迭代器返回一个数据项组成元组。 或者说 zip() 返回元组的迭代器,其中第 i 个元组包含的是每个参数迭代器的第 i 个元素。

for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']):
    print(item)
# 结果
(1, 'sugar')
(2, 'spice')
(3, 'everything nice')

如果传给 zip() 的可迭代对象长度不同: 默认情况下,zip() 在最短的迭代完成后停止。较长可迭代对象中的剩余项将被忽略,结果会裁切至最短可迭代对象的长度

>>> list(zip(range(3), ['fee', 'fi', 'fo', 'fum']))
[(0, 'fee'), (1, 'fi'), (2, 'fo')]

增加 strict=True 参数(zip() 函数在 Python 3.10 版本发生变更: 增加了 strict 参数),长度不同时抛出 ValueError 异常

>>> list(zip(('a', 'b', 'c'), (1, 2, 3), strict=True))
[('a', 1), ('b', 2), ('c', 3)]

all() 全为真

all(iterable) 如果 iterable 的所有元素均为真值(或可迭代对象为空)则返回 True 等价于:

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

any() 任意为真

any(iterable) 如果 iterable 的任一元素为真值则返回 True。 如果可迭代对象为空,返回 False。 等价于:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

str()

str(obj) 将对象转化为适于人阅读的形式。 如果 obj 类重写了 __str__ 方法,str(obj) 会调用重写的 __str__ 方法。


dir() 返回属性、方法列表

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。 如果参数包含方法 __dir__(),该方法将被调用。如果参数不包含 __dir__(),该方法将最大限度地收集参数信息。

>>> dir("str")
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

isinstance() 类型判断

isinstance(object, classinfo) 判断对象 object 是否 classinfo 类型,子类对象也是父类类型 classinfo 可以是直接或间接类名、基本类型或者由它们组成的元组

>>> isinstance(2,int)
True
>>> isinstance("str",str)
True
>>> isinstance("str",(str,int))
True

id(obj) 对象的唯一标识/内存地址


setattr() 设置对象属性

setattr(object, attr, value) attr 属性不需要存在,可以直接插入一个新属性。 参数: object 对象。 attr 字符串,属性名 value 属性值。


getattr() 获取对象属性

getattr(object, attr[, default]) 返回一个对象属性值 参数: object 对象。 attr 字符串,属性名 default 指定的属性不存在时的默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError


表达式

Python 语言参考手册 » 6. 表达式 https://docs.python.org/zh-cn/3.11/reference/expressions.html

+ 加法远算符

运算符 + (addition) 将输出其参数的和。

两个参数或者必须都为数字,或者都为相同类型的序列(列表、元素、字符串):

  • 两个参数都是数字时,两个数字将被转换为相同类型然后相加。
  • 两个参数是相同类型的序列时,将执行序列拼接操作。

浮点除法 / 与 整数除法 //

使用 / 运算符进行除法运算时,无论操作数是整数还是浮点数,结果总是浮点数。 5 / 2 的结果是 2.5

使用 // 运算符进行除法运算时,结果会向下取整到最接近的整数,也称为“地板除法”。 5 // 2 结果是 2 5 // 2.0 结果是 2.0 5.3 // 2.3 结果是 2.0

当一个操作数是浮点数时,即使另一个操作数是整数,结果也会是浮点数。 当两个操作数都是整数时,使用 / 运算符会得到浮点数结果,而使用 // 运算符则得到整数结果。


** 幂运算符

幂运算符与附带两个参数调用内置 pow() 函数具有相同的语义:结果为对其左参数进行其右参数所指定幂次的乘方运算。 数值参数会先转换为相同类型,结果也为转换后的类型。

2**3 结果 8

幂运算符的绑定比在其左侧的一元运算符更紧密 -1**2 结果是-1,幂运算符比左侧的一元操作符优先级高,所以是 -(1**2) 结果是 -1

对于 int 类型的操作数,结果将具有与操作数相同的类型,除非第二个参数为负数; 第二个参数为负数时,所有参数会被转换为 float 类型并输出 float 类型的结果。例如,10**2 返回 100,而 10**-2 返回 0.01。


比较运算

比较运算可以任意串连 例如链式比较表达式 x < y <= z 的结果等于 x < y and y <= z

x < y <= z 运算过程中 y 只会被求值一次:

  • 计算一次 x 的值,计算一次 y 的值
  • 当 x < y 值为假时,直接返回 false,z 不会被求值
  • 当 x < y 值为真时,计算一次 z 的值,使用之前计算的 y 值,检查 y <= z 的结果

x < y and y <= z 运算过程中 y 可能会被求值两次:

  • 计算一次 x 的值,计算一次 y 的值
  • 当 x < y 值为假时,直接返回 false,z 不会被求值
  • 当 x < y 值为真时,再次计算 y 值,计算 z 值,检查 y <= z 的结果

总结: 如果 a, b, c, ..., y, z 为表达式而 op1, op2, ..., opN 为比较运算符, 则 a op1 b op2 c ... y opN z 就等价于 a op1 b and b op2 c and ... y opN z,不同点在于

  • 链式比较表达式中,每个表达式最多只被求值一次。
  • and 连接比较表达式中,中间的表达式最多可能被求值2次

序列对象(列表/元组/字符串)的切片操作

https://docs.python.org/zh-cn/3.11/reference/expressions.html#slicings 切片就是在序列对象(字符串、元组或列表)中选择某个范围内的项。 切片可被用作表达式以及赋值或 del 语句的目标。

获取元组中的部分元素,又叫做切片(Slice)操作

tup[0]  # 第一个元素
tup[1:5]  # 下标1到下标4(不包括5)组成的新元组
tup[:3] # 获取第0个元素到第2个元素,即前三个元素,第一个索引是0可以省略不写
tup[1:]  # 下标1到末尾的新元组,即第二个元素到最后一个元素组成的新元组,即删除第一个元素
tup[:n] # 前n个元素组成的新元组

tup[-1] # 倒数第1个元素,倒数第1个元素的下表是-1
tup[:-1] # 删除最后一个元素得到的新元组
tup[:-2] # 从0开始到倒数第3个元素,注意-2不包括倒数第二个元素
tup[-2:] # 倒数2个元素组成的新元组
tup[-n:] # 倒数n个元素组成的新元组

tup[0: 6: 2]  # 从下标0到6(不包括6),步长为2,依次取元素组成的元组
tup[1::2]   # 从索引 1 开始以步长为 2 取值
tup[::3] # 从头到尾,步长为3取元素组成的元组,下标0第一个元素是,下标3是第二个元素。。。
tup[:] # 等于原元组,相当于复制元组

t = (1,2,3)+(4,5,6) # 元组连接(1,2,3,4,5,6)

条件表达式/三元运算符

https://docs.python.org/zh-cn/3.11/reference/expressions.html#conditional-expressions

value_if_true if condition else value_if_false 如果 condition 条件为真返回 value_if_true,否则返回 value_if_false

例如:

state = "fat" if is_fat else "not fat"
c = a if a > b else b  # c 赋值为 a,b 中的较大值

推导式

https://docs.python.org/zh-cn/3.11/reference/expressions.html#displays-for-lists-sets-and-dictionaries

推导式 comprehension

列表推导式

列表推导式(List Comprehension) [表达式 for 元素 in 可迭代对象 if 条件]

单层列表推导式示例:

def test_list_comprehension():
    # 生成平方列表
    squares = [x ** 2 for x in range(5)]  # [0, 1, 4, 9, 16]
    print(squares)  

    # 筛选偶数
    even_nums = [x for x in range(10) if x % 2 == 0]  # [0, 2, 4, 6, 8]
    print(even_nums)  

    # 连续两个 if 是逻辑与的关系
    nums = [x for x in range(10) if x % 2 == 0 if x % 3 == 0]  # [0, 6]
    print(nums)

嵌套列表推导式

# 推导式:
result = [表达式 for 外层元素 in 可迭代对象 for 内层元素 in 外层元素 if 条件]

# 等价于传统循环:
result = []
for 外层元素 in 可迭代对象:
    for 内层元素 in 外层元素:
        if 条件:
            result.append(表达式)

嵌套列表推导式执行:

  • ​​执行顺序​​:从左到右依次取出每个 for 循环执行,左侧的是外层 for 循环
  • 每层 for 循环都可以有 if 条件,if 条件仅作用于最近的左侧循环

多层嵌套列表推导式示例:

def test_nested_list_comprehension():
    # 嵌套循环(笛卡尔积)
    pairs = [(x, y) for x in [1, 2] for y in [3, 4]]  # [(1, 3), (1, 4), (2, 3), (2, 4)]
    print(pairs)

    matrix = [[1, 2], [3, 4], [5, 6]]
    # 二维列表转一维列表
    flat = [num for row in matrix for num in row]  # [1, 2, 3, 4, 5, 6]
    print(flat)
    # 奇数
    flat_odd = [num for row in matrix for num in row if num % 2 == 1]  # [1, 3, 5]
    print(flat_odd)

    matrix = [[1, 2], [3, 4, 5], [6, 7, 8, 9]]
    # if len(row) >=3 作用于外层 for 循环,if num % 2 ==1 作用于内层 for 循环
    flat = [num for row in matrix if len(row) >= 3 for num in row if num % 2 == 1]  # [3, 5, 7, 9]
    print(flat)

字典推导式

字典推导式(Dict Comprehension) {键表达式: 值表达式 for 元素 in 可迭代对象 if 条件}

字典推导式快速从可迭代对象创建字典 dict = {key: value for key in iterable}

字典推导式示例:

def test_dict_comprehension():
    # 数字映射到平方
    squares_dict = {x: x ** 2 for x in range(3)}  # {0:0, 1:1, 2:4}
    print(squares_dict)

    # 键值反转
    source_dict = {'a': 1, 'b': 2}
    reversed_dict = {v: k for k, v in source_dict.items()}  # {1: 'a', 2: 'b'}
    print(reversed_dict)

    # 过滤字典元素
    scores = {'Alice': 85, 'Bob': 60, 'Charlie': 92}
    passed = {k: v for k, v in scores.items() if v >= 90}  # {'Charlie': 92}
    print(passed)

集合推导式

集合推导式(Set Comprehension) {表达式 for 元素 in 可迭代对象 if 条件}

可利用集合的元素唯一属性去重

集合推导式示例:

def test_set_comprehension():
    # 生成不重复的平方数
    unique_squares = {x ** 2 for x in [1, -1, 2, -2]}  # {1, 4}
    print(unique_squares)

    # 列表去重
    source_list = ['apple', 'banana', 'orange', 'apple']
    unique_fruits = {fruit for fruit in source_list}  # {'banana', 'orange', 'apple'}
    print(unique_fruits)

生成器表达式

https://docs.python.org/zh-cn/3.11/reference/expressions.html#generator-expressions

生成器表达式(Generator Expression)是 Python 中用于​​创建生成器对象​​的简洁语法结构。 生成器表达式句法与推导式相同,区别在于它是用圆括号而不是用方括号或花括号括起来的 (expression for item in iterable if condition)

惰性求值 (Lazy Evaluation)​ 相比于推导式一次性生成全部数据,生成器表达式是惰性求值的,仅在迭代时动态生成值,而非一次性创建完整数据结构。

单次使用 生成器只能迭代一次(如需重用需重新创建)。

内存高效​ 每次只产生一个值,内存占用恒定,适合处理大型/无限数据流。

def test_generator():
    gen = (x for x in range(3))
    print(type(gen))  # <class 'generator'>
    print(list(gen))  # [0, 1, 2]
    print(list(gen))  # [] (生成器已耗尽)

yield 表达式

https://docs.python.org/zh-cn/3.11/reference/expressions.html#yield-expressions

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

生成器使用 yield 语句而不是 return 语句返回结果, yield 语句一次返回一个结果,在每个结果中间,会暂停并保存当前所有的运行信息,以便下一次执行 next() 方法时从当前位置继续运行。

yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。


lambda 表达式

https://docs.python.org/zh-cn/3.11/reference/expressions.html#lambda


in/not in 成员检测运算

https://docs.python.org/zh-cn/3.11/reference/expressions.html#membership-test-operations

运算符 in 和 not in 用于成员检测。

  • 如果 x 是 s 的成员则 x in s 求值为 True,否则为 False
  • x not in s 返回 x in s 取反后的值。

所有内置序列和集合类型以及字典都支持此运算。

对于字典来说 in 检测其是否有给定的键。

对于 list, tuple, set, frozenset, dict 或 collections.deque 这样的容器类型,表达式 x in y 等价于 any(x is e or x == e for e in y)

对于字符串和字节串类型来说,当且仅当 x 是 y 的子串时 x in y 为 True 一个等价的检测是 y.find(x) != -1 空字符串总是被视为任何其他字符串的子串,因此 "" in "abc" 将返回 True

对于定义了 __contains__() 方法的用户自定义类来说,如果 y.__contains__(x) 返回真值则 x in y 将返回 True,否则返回 False。

对于未定义 __contains__() 但定义了 __iter__() 的用户自定义类来说,如果在迭代 y 期间产生了值 z 使得表达式 x is z or x == z 为真值,则 x in y 将为 True。 如果在迭代期间引发了异常,则将等同于 in 引发了该异常。


* 解包

把 容器 中元素当成一个个元素取出来,这个过程就是解包(Unpacking),可迭代对象都可以被解包。

>>> a,b,c=[1,2,3]
>>> a
1

多变量赋值

Python 中的多变量赋值本质也是解包操作:

>>> a,b="str",3
>>> a
'str'
>>> b
3

*iter 可迭代对象解包

PEP 3132 允许左边变量个数小于右边,剩余参数都分配给带星号 * 的变量

>>> a,*b,c=[1,2,3,4]
>>> a
1
>>> b
[2, 3]
>>> c
4

**dict 字典解包

**dict 是一种字典解包操作符,主要用于将字典的键值对展开为独立的元素。

keys = [*dict] 对字典使用 *,会解包出 ​key 的列表

用途1,合并字典:

my_dict = {"a": 1, "b": 2}
# 合并字典
merged_dict = {**my_dict, "c": 3}  # 结果:{'a':1, 'b':2, 'c':3}

用途2,函数传参

# 函数参数传递
def func(a, b):
    print(a + b)
func(**my_dict)  # 等效于 func(a=1, b=2)

bool 布尔类型

在Python中,假值包括

  • False
  • None
  • 0
  • '' 空字符串
  • [] 空列表
  • {} 空字典
  • () 空元组
  • set() 空集合

Python 中,True 等于 1,而 False 等于 0 所以 fitness = ("skinny", "fat")[fat_bool] 可执行:

  • 当 fat_bool 为 True/1 时,返回元组的第 1 个元素,即 "fat"
  • 当 fat_bool 为 False/0 时,返回元组的第 0 个元素,即 "skinny"

上面这种用法和 if 条件表达式并不等价:

  • 元组中的两个表达式总是都被计算
  • if 表达式支持短路:if 条件为 true 时并不执行 else 后的表达式

例如:

condition = True
print(2 if condition else 1 / 0)  # condition 为 true 时并不执行 else 后的表达式,输出: 2
print((1 / 0, 2)[condition])  # 输出 ZeroDivisionError 异常

字符串

https://docs.python.org/zh-cn/3.11/library/stdtypes.html#text-sequence-type-str

Python 中以单引号或双引号引起来的是字符串,例如 '123' "abc"

如果字符串内本身包含单引号或双引号,可以对引号进行转义 \' \"

v1 = 'str have "double quotes"'
v2 = "str have 'single quotes'"
v3 = 'str have both "double quotes" and \'single quotes(escape)\''
v4 = "str have both \"double quotes(escape)\" and 'single quotes'"

不可变性:Python 中的字符串是不可变对象,创建后无法修改,所有字符串操作方法都是返回一个新的字符串,不会修改原字符串。 序列操作:Python 中的字符串也是序列,也支持索引和切片操作 Unicode支持​​:Python 3 中所有字符串都是 Unicode

字符串格式化

age = 30
name = masikkk
str = "我是 %s, 我今年 %d" % (name, age)

f格式化字符串(3.6+)

f-string 是 python3.6 之后版本添加的,称之为字面量格式化字符串,是新的格式化字符串的语法。 f-string 格式化字符串以 f 开头,后面跟着字符串,字符串中的表达式用大括号 {} 包起来,它会将变量或表达式计算后的值替换进去。

实例如下:

name = 'Runoob'
str1 = f'Hello {name}'  # 替换变量
str2 = f'{1+2}'         # 使用表达式

score = 85
print(f"Grade: {'A' if score >= 90 else 'B' if score >= 80 else 'C'}")

格式化

pi = 3.141592653589793
print(f"Pi to two decimal places is {pi:.2f}")   # 3.14
print(f"Pi in scientific notation is {pi:.2e}")  # 3.14e+00

''' '''多行字符串

1、字符串常量分多行写 代码中字符串常量太长超过行宽的话,可以 \ 换行或 () 分多行写,例如:

s1 = 'straaa' \
      'strbbb'
s2 = 'str111' 'str222'
s3 = ('strfff' 'strddd')
s4 = (
    'streeee'
    'strrrrr'
)

但这并不是多行字符串,只要字符串中没有 \n 输出后还是一行。

2、多行字符串块 3个单引号 '''...''' 或3个双引号 """...""" 包围的是多行字符串,例如:

    multi_str = '''line1
        line2
    line3
'''
print(multi_str)

输出结果:

line1
        line2
    line3

u字符串(Unicode)

u/U 表示 Unicode 字符串 引号前小写的 u 表示这里创建的是一个 Unicode 字符串。

us = u'Hello World !'

如果你想加入一个特殊字符,可以使用 Python 的 Unicode-Escape 编码。如下例所示:

us = u'Hello\u0020World !'

被替换的 \u0020 标识表示在给定位置插入编码值为 0x0020 的 Unicode 字符(空格符)。

r字符串(非转义)

r'' 内部的字符串默认不转义,原样输出 \t \n \\

r1 = r'\t\n\\\'\"'
print(r1)  # 输出 \t\n\\\'\"

find() 查找子串位置(未找到返回-1)

str.find(sub[, start[, end]]) 返回子字符串 sub 在 s[start:end] 切片内被找到的最小索引。

可选参数 start 与 end 会被解读为切片表示法。

  • start(可选):开始搜索的索引位置,默认为0
  • end(可选):结束搜索的索引位置,默认为字符串长度

如果 sub 未被找到则返回 -1。 find() 方法应该只在你需要知道 sub 所在位置时使用。 要检查 sub 是否为子字符串,请使用 in 操作符

>>> 'python'.find('th')
2
>>> 'python'.find('the')
-1

index() 查找子串位置(未找到抛异常)

index(sub[, start[, end]]) index() 方法与 find() 类似,也是用于查找子字符串的第一次出现位置。 如果找到子字符串,返回第一次出现的索引位置。 如果未找到,抛出 ValueError 异常

>>> 'python'.index('the')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found

strip() 移除首尾空白

strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。 注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。 str.strip([chars]); chars -- 移除字符串头尾指定的字符序列。 返回移除字符串头尾指定的字符生成的新字符串。

str.strip('0') 移除str收尾的字符0 str.strip() 移除str收尾的空白(空格、换行)

lstrip() 移除开头空白

str.lstrip([chars]) 参数:chars 可选,一个字符串,指定需要移除的字符集合。如果未提供此参数,则默认移除空白字符(空格、换行符\n、制表符\t)。

text.lstrip('abc') 移除开头的 a 或 b 或 c 字符


split() 分割字符串

str.split(separator="", maxsplit=-1) split() 通过指定分隔符对字符串进行切片,如果参数 maxsplit 有指定值,则分隔 maxsplit 次得到 maxsplit+1 个子字符串 参数: separator 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。 maxsplit 分割次数。默认为 -1, 即分隔所有。maxsplit=1表示分割1次结果是2个子串,maxsplit=n表示分割n次结果是n+1个子串。 返回分割后的字符串列表。

str = "Line1-abcdef \nLine2-abc \nLine4-abcd";
print str.split();       # 以空格为分隔符,包含 \n
print str.split(' ', 1); # 以空格为分隔符,分隔成两个

join() 串接

str.join(sequence) 将序列 sequence 中的元素以指定的字符 str 连接生成一个新的字符串

symbol = "-";
seq = ("a", "b", "c"); # 字符串序列
print symbol.join( seq ); # 结果 a-b-c

ljust() 补全到指定长度

str.ljust(width[, fillchar]) 参数:

  • width:指定返回字符串的总长度。
  • fillchar:(可选)指定填充字符,默认为空格字符。

返回值:返回一个新的字符串,它是原字符串左对齐,并用 fillchar(默认空格)填充至 width 指定的长度。

例如,将人名字符串都补全到长度10,用-填充

def test_str_ljust():
    names = ["Alice", "Bob", "Charlie", "CristianoRonaldo"]
    width = 10
    for name in names:
        print(name.ljust(width, '-'))

结果:

Alice-----
Bob-------
Charlie---
CristianoRonaldo

replace() 子串替换

str.replace(old, new[, count]) 用 new 替换子字符串 old 的所有出现次数,并返回该字符串的副本。 如果给定了可选参数 count,则只替换前 count 次出现的字符串。

>>> 'hello'.replace('l', 'z', 1)  # 替换1次
'hezlo'
>>> 'hello'.replace('l', 'z')  # 全部替换
'hezzo'

tuple 元组(不可变)

Python 的元组与列表类似,不同之处在于元组的元素不能修改也不能删除。 元组使用小括号,列表使用方括号。 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。

tup0 = ()  # 空元组
tup1 = ('Google', 'Runoob', 1997, 2000)
tup2 = (1, 2, 3, 4, 5 )
tup3 = "a", "b", "c", "d"   #  不需要括号也可以

元组中只包含一个元素时,需要在元素后面添加逗号,否则括号会被当作运算符使用:

tup1 = (50) # 不加逗号,类型为整型
tup2 = (50,) # 加上逗号,类型为元组

list 数组/列表(可变)

定义列表

classmates = ['Michael', 'Bob', 'Tracy']
L = ['Apple', 123, True]  # 列表中元素类型可以不同
L = []  # len(L)等于0
l = list(tuple01)  # 元组转列表

len() 可以获取列表元素个数

访问列表元素

classmates[0]  # 下标从0开始
classmates(len(classmates) - 1) # 最后一个元素下标是 len(classmates) - 1
classmates[-1] # -1 表示最后一个元素,-n表示倒数第n个元素

操作列表元素

classmates.append('Adam')  # 插入到末尾
classmates.insert(1, 'Jack')  # 插入到下标位置1
classmates.pop()  # 删除末尾元素
classmates.pop(i)  # 删除下标位置i的元素
classmates[i] = 'Sarah'  # 替换下标i的元素

list 判空

1、用 len 判断

if len(mylist):
    # list is not empty
else:
    # list is empty

2、由于一个空 list 本身等同于 False,可直接判断

if mylist:
    # list is not empty
else:
    # list is empty

sort() 排序

list.sort(key=None, reverse=False) sort(*, key=None, reverse=False) 此方法会对列表进行原地排序,只使用 < 来进行各项间比较。 异常不会被屏蔽 —— 如果有任何比较操作失败,整个排序操作将失败(而列表可能会处于被部分修改的状态)。 sort() 接受两个仅限以关键字形式传入的参数

key 指定带有一个参数的函数,用于从每个列表元素中提取比较键 (例如 key=str.lower)。 对应于列表中每一项的键会被计算一次,然后在整个排序过程中使用。 默认值 None 表示直接对列表项排序而不计算一个单独的键值。 reverse 为一个布尔值。 如果设为 True,则每个列表元素将按反向顺序比较进行排序。

sort() 方法确保是稳定的。


dict 字典

d = {key1 : value1, key2 : value2, key3 : value3 } 字典的每个键值 key=>value 对用冒号 : 分割,每个对之间用逗号(,)分割,整个字典包括在花括号 {} 中

Python 3 中 dict.keys(), dict.values() 和 dict.items() 返回的都是视图对象(view objects),提供了字典实体的动态视图,这就意味着字典改变,视图也会跟着变化。 不能对视图对象进行任何的修改,视图对象都是只读的。尝试直接修改会报错 AttributeError 视图对象不是列表,不支持索引,可以使用 list() 来转换为列表。 Python 2 中 dict.keys(), dict.values() 和 dict.items() 是直接返回列表


keys() 键视图对象

keys() 返回字典中所有 键 组成的视图对象

两次 dict.keys() 等值比较结果总是 True dict.keys() 返回的视图对象是可哈希的(hashable),并且两个键视图对象的比较是基于字典键的集合是否相同。 当比较两个键视图(如 data.keys() == data.keys())时,Python 会检查它们是否包含相同的键集合。由于两次调用 data.keys() 返回的视图对象动态绑定到同一个字典,它们的键集合必然相同,因此结果为 True。

def test_keys_values():
    data = {"a": 1, "b": 2, "c": 3}
    print(data.keys() == data.keys())  # True
    keys = data.keys()
    print(type(keys))  # <class 'dict_keys'>
    print(keys)  # dict_keys(['a', 'b', 'c'])

values() 值视图对象

values() 返回字典中所有 值 组成的视图对象

两次 dict.values() 等值比较结果总是 False dict.values() 返回的值视图是动态生成的迭代器,每次调用 data.values() 会生成一个新的迭代器对象。 虽然两个迭代器的内容相同(即字典的值相同),但 Python 的默认比较行为对于迭代器是检查它们是否是同一个对象(即身份比较 is),而不是值比较。由于两次调用 data.values() 返回的是不同的迭代器对象,因此结果为 False。

def test_values():
    data = {"a": 1, "b": 2, "c": 3}
    print(data.values() == data.values())  # False
    values = data.values()
    print(type(values))  # <class 'dict_values'>
    print(values)  # dict_values([1, 2, 3])

items() 键值对元组

返回一个视图对象(view object),包含字典中所有的键值对,形式为 (key, value) 的元组。

使用 k,v 遍历 items() 返回的元组:

data = {"a": 1, "b": 2, "c": 3}
for key, value in data.items():
    print(key, value)

输出: a 1 b 2 c 3

直接遍历 items() 返回的元组:

data = {"a": 1, "b": 2, "c": 3}
for entry in data.items():
    print(entry)

输出: ('a', 1) ('b', 2) ('c', 3)

判断dict中是否有某个key

Python2 中使用 has_key 方法,此方法再 Python3 中已删除,不建议使用。

user_info = {'name': 'nock', 'age': 18}
user_info.has_key('job')

更推荐使用 innot in 方法,再 Python2 和 Python3 中都适用:

user_info = {'name': 'nock', 'age': 18}
if 'name' in user_info:
  print('exist')
if 'class' not in user_info:
  print('not exist')

collections

https://docs.python.org/zh-cn/3.11/library/collections.html collections 是 Python 标准库中一个强大的内置模块,提供了 ​​高效且专用的容器数据类型​​,用于替代通用内置容器(如 dict、list、set、tuple)在特定场景下的不足。

defaultdict 带默认值的字典

访问缺失键时自动生成默认值,避免 KeyError

def test_defaultdict():
    int_value_dict = defaultdict(int)  # 默认值为 int(自动为不存在的key赋值0)
    int_value_dict['k1'] += 1
    print(f"k1: {int_value_dict['k1']}")
    print(f"k2: {int_value_dict['k2']}")  # 0

    list_value_dict = defaultdict(list)  # 默认值为 list(自动为不存在的key创建空列表)
    list_value_dict['k1'].append('v1')
    print(f"k1: {list_value_dict['k1']}")
    print(f"k2: {list_value_dict['k2']}")  # []

    set_value_dict = defaultdict(set)  # 默认值为 set(自动为不存在的key创建空集合)
    set_value_dict['k1'].add('v1')  # 直接添加即可
    print(f"k1: {set_value_dict['k1']}")
    print(f"k2: {set_value_dict['k2']}")
    print(f"k3: {set_value_dict['k3']}")  # set() 

with as 上下文管理器

with as 是 Python 中的 上下文管理器 Context Manager,类似 java 中的 try with resource 原理: with 所求值的对象必须有一个 __enter__() 方法,一个 __exit__() 方法。 __enter__() 方法会在 with 语句进入时被调用,其返回值会赋给 as 关键字后的变量;而 __exit__() 方法会在 with 语句块退出后自动被调用。

@contextlib.contextmanager 装饰器

@contextlib.contextmanager 是一个装饰器,由它修饰的方法会有两部分构成,中间由 yield 关键字分开。由此方法创建的上下文管理器,在代码块执行前会先执行 yield 上面的语句;在代码块执行后会再执行 yield 下面的语句。 yield 上面的语句就如同之间介绍过的 __enter__() 方法,而 yield 下面的语句就如同 __exit__() 方法。


全局解释器锁 GIL

全局解释器锁(Global Interpreter Lock, GIL) 是 CPython 解释器中的一种互斥锁,确保同一时刻仅有一个线程执行 Python 字节码。

线程执行 Python 代码前需先获取 GIL,执行完成后释放。其他线程需等待锁的释放才能继续执行。 释放时机:

  • 时间片耗尽:每个线程默认执行约5毫秒后释放GIL(具体时间因 Python 版本而异)
  • IO操作阻塞:如文件读写、网络请求等,线程主动释放 GIL

GIL 对多线程编程的影响

  • CPU密集型任务:多线程无法并行执行,无法利用多核CPU,甚至可能因锁竞争导致性能低于单线程
  • IO密集型任务:线程在等待IO时释放GIL,其他线程可继续执行,多线程能有效提升效率

绕过 GIL 限制的方法

1、多进程 使用 multiprocessing 模块,每个进程拥有独立 GIL,实现真正的多核并行。

2、C扩展与第三方库 关键代码用 C/C++ 编写(如NumPy、Pandas),绕过 GIL 限制。 深度学习框架(如TensorFlow、PyTorch)在底层实现并行计算。

3、异步编程 使用 asyncio 库通过协程处理高并发 IO 任务

4、替代 CPython 解释器 Jython(基于Java)或 IronPython(基于.NET)无 GIL,但缺乏对部分 CPython 库的兼容性


读写文件

读文件

通常而言,读取文件有以下几种方式: 一次性读取所有内容,使用 read()readlines() 按字节读取,使用 read(size) 按行读取,使用 readline()

文件打开模式:

  • r 读模式
  • w 写模式
  • a 追加模式
  • b 二进制模式(可添加到其他模式中使用)
  • + 读/写模式(可添加到其他模式中使用)

推荐使用 with 语句,可自动调用 close 方法

读取文本全部内容

with open('/path/to/file', 'r', encoding='utf-8') as f:
    data = f.read()

按行读取文本内容

with open('/Users/user/file.csv', 'r', encoding='utf-8') as f:
    for line in f.readlines():
        print(line)

按字节读取

with open('path/to/file', 'r') as f:
    while True:
        piece = f.read(1024)        # 每次读取 1024 个字节(即 1 KB)的内容
        if not piece:
            break
        print piece

写文件

with open('/Users/ethan/data2.txt', 'w') as f:
    f.write('one\n')
    f.write('two')

如果上述文件已存在,则会清空原内容并覆盖掉; 如果上述路径是正确的(比如存在 /Users/ethan 的路径),但是文件不存在(data2.txt 不存在),则会新建一个文件,并写入上述内容; 如果上述路径是不正确的(比如将路径写成 /Users/eth ),这时会抛出 IOError;

如果我们想往已存在的文件追加内容,可以使用 'a' 模式,如下:

with open('/Users/ethan/data2.txt', 'a') as f:
    f.write('three\n')
    f.write('four')

文件编码

my.py 中有中文,但是没有加编码声明,会报如下错误

python my.py
  File "my.py", line 11
SyntaxError: Non-ASCII character '\xe4' in file my.py on line 11, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

解决,my.py脚本第一行增加 # -*- coding: utf-8 -*-

PEP 263 -- Defining Python Source Code Encodings https://www.python.org/dev/peps/pep-0263/


异常处理

try:
  Normal execution block
except A:
  Exception A handle
except B:
  Exception B handle
except:
  Other exception handle
else:
  if no exception,get here
finally:
  print("finally")   

1、正常执行的程序在 try 下面的Normal execution block执行块中执行,在执行过程中如果发生了异常,则中断当前在Normal execution block中的执行跳转到对应的异常处理块中开始执行; 2、python 从第一个 except X 处开始查找,如果找到了对应的 exception 类型则进入其提供的 exception handle 中进行处理,如果没有找到则直接进无具体异常类型的 except 块处进行处理。无异常 except 块是可选项,如果没有提供,该 exception 将会被提交给 python 进行默认处理,处理方式则是终止应用程序并打印提示信息; 3、如果在 Normal execution block 执行块中执行过程中没有发生任何异常,则在执行完 Normal execution block 后会进入 else 执行块中(如果存在的话)执行。 4、无论是否发生了异常,只要提供了 finally 语句,以上 try/except/else/finally 代码块执行的最后一步总是执行 finally 所对应的代码块。

注意: 1、在上面所示的完整语句中 try/except/else/finally 所出现的顺序必须是 try -> except X -> except -> else -> finally,即所有的 except 必须在 else 和 finally 之前,else(如果有的话)必须在 finally 之前,而 exceptX 必须在 except 之前。否则会出现语法错误。 2、对于上面所展示的 try/except 完整格式而言,else 和 finally 都是可选的,而不是必须的,但是如果存在的话 else 必须在 finally 之前,finally(如果存在的话)必须在整个语句的最后位置。 3、在上面的完整语句中,else 语句的存在必须以 except X 或者 except 语句为前提,如果在没有 except 语句的 try block 中使用 else 语句会引发语法错误。也就是说 else 不能与 try/finally 配合使用。


python 命令

python -m 模块当脚本执行

-m mod : run library module as a script (terminates option list) 将一个模块当做脚本执行,也就是允许在命令行中直接运行一个模块中的代码,而无需显式地调用模块内部的某个函数或方法 目的是帮助我们更方便的执行 python 库中的脚本,各种库也可以利用这种方式来提供一些实用的、可立即执行的功能。

python -m 从 Python 2.5 版本引入,具体见 PEP 338 PEP 338 – Executing modules as scripts https://peps.python.org/pep-0338/

python 中有一个叫做 runpy 的模块,这就是 python -m 的具体实现,执行 python -m modulename 等同于运行 runpy.run_module(modulename) 如果包含下面代码,执行 python -m 时就会执行 main() 方法内容,这部分代码通常被定义为模块被直接运行时应该执行的代码

if __name__ == "__main__": 
    main()

例,如果你有一个叫 foo 的包,有一个叫 bar 的子包和一个叫 baz 的模块,可以这样执行: python -m foo.bar.baz

例1,python2中启动一个简单的http服务器 python -m SimpleHTTPServer

例2,python3 中在 8000 端口上启动一个简单的http服务器,可以用于文件共享 python -m http.server 8000

例3,创建虚拟环境(venv是python3自带的环境管理工具,和virtualenv用法类似) python -m venv

例4,启动IDLE,一个python自带可开发环境 python -m idlelib

Python -m 参数解析 https://a7744hsc.github.io/python/2018/05/03/Run-python-script.html

更多 python -m 实用包: awesome-python-modules-as-script https://github.com/cassiobotaro/awesome-python-modules-as-script

python -m xx 与 python xx.py 区别

1、通过 python -m 执行一个包内脚本会首先将执行 package 的 __init__.py 文件,并且 __package__ 变量被赋上相应的值;而 python xxx.py 方式不会执行 __init__.py 并且 __package__ 变量为 None

2、两种执行方法的 sys.path 不同,Python 中的 sys.path 是 Python 用来搜索包和模块的路径。通过 python -m 执行一个脚本时会将当前路径加入到系统路径中,而使用 python xxx.py 执行脚本则会将脚本所在文件夹加入到系统路径中。

python -V 查看python版本

python -Vpython --version

$ python -V
Python 2.7.16

Python2 升级 Python3

在 Python 2 中,print 是一个语句而非函数,因此不需要使用括号。例如: print "Hello, World!" Python 2 中的 print 语句支持逗号分隔的多个参数,它们会在输出时自动用空格分隔,并在末尾添加一个换行符。 例如:print "Hello,", "World!" 会输出 Hello, World!

在 Python 3 中,print 被重新定义为一个函数,因此必须使用括号。例如: print("Hello, World!") 作为函数,print 可以接受多个参数,这些参数会作为元组传递给 sepend 等关键字参数,允许更灵活的输出格式控制。 例如: print("Hello,", "World!", sep=" ") 会输出 Hello, World!, 而 print("Hello,", "World!", end="!\n") 则会在输出末尾添加自定义的结束字符。

2to3.py

几乎所有的Python2程序都需要一些修改才能正常地运行在Python3的环境下。为了简化这个转换过程,Python 3自带了一个叫做2to3的实用脚本(Utility Script),这个脚本会将你的Python 2程序源文件作为输入,然后自动将其转换到Python 3的形式。这个脚本的位置位在Python安装的根目录下的 Python35\Tools\scripts\目录中。

-w参数,不加-w参数,则默认只是把转换过程所对应的diff内容打印输出到当前窗口而已。加了-w,就是把改动内容,写回到原先的文件了,同时会将原文件保存为文件名.bak做备份。

将当前目录下的python2源码transform.py转换为python3: 2to3.py -w transform.py 执行完后transform.py变为python3格式源码,同时生成transform.py.bak

将E:\ipv6--master\下的所有python2代码转换为python3: python 2to3.py -w E:\ipv6--master\