python学习

1、基础语法

Python是一种高级编程语言,以其可读性和简洁的语法而闻名。下面我将给出Python语言的一些基本语法和示例。

input和print

在Python中,`print`和`input`是两个基础且重要的函数,它们在基本输入输出操作中扮演着中心角色。

### `print` 函数
#`print`函数用来在标准输出(通常是屏幕)上打印指定的文字或变量内容。
#### 用法示例:
print("Hello, World!")  # 打印字符串
name = "Alice"
print(name)  # 打印变量
number = 42
print("The answer is:", number)  # 打印字符串和变量的组合
# 指定分隔符
print("A", "B", "C", sep="-")   # 输出: A-B-C
# 在最后指定结束字符串,默认为换行符'\n'
print("Hello, World!", end=" ")  # 结束符替换为一个空格
print("This is on the same line.")
# 打印多个值
x, y, z = 1, 2, 3
print(x, y, z)
#### 原理:
#`print`实际上是一个内置函数,它能够将传递给它的对象转换为字符串形式(利用`str`函数),然后写入#到标准输出流(`sys.stdout`)。如果你没有指定`sep`参数,默认情况下在对象间会添加一个空格作为分#隔,在末尾添加换行符。
### `input` 函数
#`input`函数用来从标凈输入(通常是键盘)接收用户输入的字符串。
#### 用法示例:
name = input("Enter your name: ")  # 提示用户输入姓名
print("Hello,", name)  # 打印欢迎信息和用户输入的姓名
#### 原理:
#当`input()`函数被调用时,程序暂停等待用户输入文字并敲击回车键。用户输入的任何内容都将被作为字符串返回,并可以被赋值给一个变量。如果在`input()`函数中提供了字符串参数,该字符串将被显示为提示信息,指引用户进行输入。

#需要注意的是,`input`接收到的输入内容始终是字符串类型。如果你需要其他类型的数据,那么你必须对用户的输入进行类型转换,比如使用`int()`或`float()`函数将输入转换为整数或浮点数:
age = int(input("Enter your age: "))  # 将用户输入的字符串转换为整数

这是`print`和`input`函数的基本用法和原理。它们都是Python编程的基础工具,适用于各种控制台应用程序和基本用户交互场景。

注释

在 Python 中,有两种形式的注释:

 1. 单行注释:在 Python 中, 我们使用 `#` 符号进行单行注释。在 `#` 符号后面直到行尾的所有内容都会被 Python 解释器忽略。

2. 多行注释:在 Python 中,我们通常使用三重双引号 `"""` 或三重单引号 `'''` 来建立多行注释,被注释内容可以跨越多行。


# 这是一个单行注释
print("Hello, World!")  # 这是在代码行尾的一个注释
"""
这是一个多行注释
可以跨越多行
例如像这样
"""
print("Hello, World!")
'''
这也是一个多行注释
同样可以跨越多行
例如像这样
'''
print("Hello, World!")

注释是一种使代码更易于理解和维护的工具,可以用来解释代码的工作原理、算法的思路,或者指示一个待解决的问题等。编写好注释有助于其他阅读你的代码的人(包括未来的你自己)理解你的程序。

变量和数据类型

在Python中,你不需要显式声明变量的数据类型,解释器会自动推断。

# 数值类型
int_variable = 10                # 整型
float_variable = 20.55           # 浮点型
complex_variable = 3.14j         # 复数

# 布尔类型
bool_variable = True             # 布尔型

# 文本类型
str_variable = "Hello, World!"   # 字符串

# 序列类型
list_variable = [1, 2, 3, 4, 5]  # 列表
tuple_variable = (1, 2, 3, 4, 5) # 元组
range_variable = range(5)        # 范围

# 映射类型
dict_variable = {'a': 1, 'b': 2} # 字典

# 集合类型
set_variable = {1, 2, 3}         # 集合
frozenset_variable = frozenset({1, 2, 3}) # 不可变集合

# 二进制类型
bytes_variable = b"Hello"        # 字节
bytearray_variable = bytearray(5)# 字节数组
memoryview_variable = memoryview(bytes_variable) # 内存视图

运算符

在 Python 中,运算符用于执行各种运算,如算术运算、逻辑运算、比较/关系运算等。以下是 Python 中各类运算符的介绍和演示:

1. 算术运算符:
- 加法 (`+`): `3 + 5` 结果是 `8`
- 减法 (`-`): `5 - 3` 结果是 `2`
- 乘法 (`*`): `3 * 5` 结果是 `15`
- 除法 (`/`): `15 / 3` 结果是 `5.0`
- 整数除法/地板除 (`//`): `15 // 2` 结果是 `7`
- 模运算/求余 (`%`): `15 % 4` 结果是 `3`
- 指数 (`**`): `2 ** 3` 结果是 `8`

2. 赋值运算符:
- 直接赋值 (`=`): `a = 5` 将 `5` 赋给变量 `a`
- 加法赋值 (`+=`): `a += 3` 等同于 `a = a + 3`
- 减法赋值 (`-=`): `a -= 2` 等同于 `a = a - 2`
- 乘法赋值 (`*=`): `a *= 2` 等同于 `a = a * 2`
- 除法赋值 (`/=`): `a /= 3` 等同于 `a = a / 3`
- 整数除法赋值 (`//=`): `a //= 3` 等同于 `a = a // 3`
- 模运算赋值 (`%=`): `a %= 3` 等同于 `a = a % 3`
- 指数赋值 (`**=`): `a **= 3` 等同于 `a = a ** 3`

3. 比较/关系运算符:
- 等于 (`==`): `5 == 5` 结果是 `True`
- 不等于 (`!=`): `5 != 5` 结果是 `False`
- 大于 (`>`): `5 > 3` 结果是 `True`
- 小于 (`<`): `3 < 5` 结果是 `True`
- 大于等于 (`>=`): `5 >= 5` 结果是 `True`
- 小于等于 (`<=`): `3 <= 5` 结果是 `True`

4. 逻辑运算符:
- 逻辑与 (`and`): `True and False` 结果是 `False`
- 逻辑或 (`or`): `True or False` 结果是 `True`
- 逻辑非 (`not`): `not True` 结果是 `False`

5. 位运算符 (对整数的二进制位进行操作):
- 按位与 (`&`): `5 & 3` 结果是 `1` (二进制表达: 0101 & 0011 = 0001)
- 按位或 (`|`): `5 | 3` 结果是 `7` (二进制表达: 0101 | 0011 = 0111)
- 按位异或 (`^`): `5 ^ 3` 结果是 `6` (二进制表达: 0101 ^ 0011 = 0110)
- 按位取反 (`~`): `~5` 结果是 `-6` (因为 ~0101 = 1010, 在二进制补码中表示 -6)
- 左移 (`<<`): `5 << 1` 结果是 `10` (二进制表达: 0101 << 1 = 1010)
- 右移 (`>>`): `5 >> 1` 结果是 `2` (二进制表达: 0101 >> 1 = 0010)

6. 成员运算符:
- `in`: `5 in [1,2,3,4,5]` 结果是 `True`
- `not in`: `5 not in [1,2,3,4]` 结果是 `True`

7. 身份运算符:
- `is`: `a is b` 检查 a 和 b 是否引用自同一个对象
- `is not`: `a is not b` 检查 a 和 b 是否引用自不同对象

8. 条件表达式 (三元运算符):
`x if C else y` 如果条件C为True,则表达式的结果是x,否则是y。

#x**2 for x in range(10)` 使用的是列表推导式的形式,也就是用到了算术运算符 (** 指数) 和 for 循#环,但没有直接出现在运算符的分类中。以下是该列表推导式的展开并运用算术运算符的例子:
squared_numbers = [x**2 for x in range(10)]
print(squared_numbers)
#运行这段代码会打印出一个列表,其中包含0到9每个数字的平方。

作用域 

局部作用域

在函数内部创建的变量、函数内部的函数属于该函数的局部作用域,并且只能在该函数内部使用。

全局作用域

在 Python 代码主体中创建的变量是全局变量,属于全局作用域。

全局变量在任何范围(全局和局部)中可用。在函数外部创建的变量是全局变量,任何人都可以使用

命名变量

如果在函数内部和外部操作同名变量,Python 会将它们视为两个单独的变量,一个在全局范围内可用(在函数外部),而一个在局部范围内可用(在函数内部)

Global 关键字

如果您需要创建一个全局变量,但被卡在本地作用域内,则可以使用 global 关键字。

global 关键字使变量成为全局变量(如在函数内创建global),要在函数内部更改全局变量的值,请使用 global 关键字引用该变量。

模块

pip install camelcase导camelcase包
pip uninstall camelcase删camelcase包
pip list列出已安装的包
pip --version看pip版本

在文件 mymodule.py 中保存代码:

person1 = {
  "name": "Bill",
  "age": 63,
  "country": "USA"
}

导入名为 mymodule 的模块,并访问 person1 字典:

import mymodule
a = mymodule.person1["age"]
print(a)

为 mymodule 创建别名 mx:

import mymodule as mx
a = mx.person1["age"]
print(a)

有一个内置函数可以列出模块中的所有函数名(或变量名):dir() 函数。dir() 函数可用于所有模块,也可用于您自己创建的模块。

您可以使用 from 关键字选择仅从模块导入部件。在使用 from 关键字导入时,请勿在引用模块中的元素时使用模块名称

仅从模块导入 person1 字典:

from mymodule import person1

print (person1["age"])
import datetime
x = datetime.datetime.now()
print(x)
print(x.year)
print(x.strftime("%A"))
x = datetime.datetime(2020, 5, 17)
print(x)
print(x.strftime("%B%%%c"))
#把 JSON 转换为 Python:
import json
# 一些 JSON:
x =  '{ "name":"Bill", "age":63, "city":"Seatle"}'
# 解析 x:
y = json.loads(x)
# 结果是 Python 字典:
print(y["age"])
# 转换为 JSON:
y = json.dumps(x)
# 结果是 JSON 字符串:
print(y)
print(json.dumps({"name": "Bill", "age": 63}))
print(json.dumps(["apple", "bananas"]))
print(json.dumps(("apple", "bananas")))
#print(json.dumps({1,2,3}))#set不能转json
print(json.dumps("hello"))
print(json.dumps(42))
print(json.dumps(31.76))
print(json.dumps(True))
print(json.dumps(False))
print(json.dumps(None))
x = {
 "name": "Bill",
 "age": 63,
 "married": True,
 "divorced": False,
 "children": ("Jennifer","Rory","Phoebe"),
 "pets": None,
 "cars": [
  {"model": "Porsche", "mpg": 38.2},
  {"model": "BMW M5", "mpg": 26.9}
 ]
}
print(json.dumps(x))
#使用 indent 参数定义缩进数
#使用 separators 参数来更改默认分隔符:默认值为(", ", ": "),这意味着使用逗号和空格分隔每个对象,使用冒号和空格将键与值分开
#使用 sort_keys 参数来指定是否应对结果进行排序(默认按字母序)
print(json.dumps(x, indent=4, separators=(". ", " = "),sort_keys=True))
#检索字符串以查看它是否以 "China" 开头并以 "country" 结尾:
import re
#导入 re 模块后,就可以开始使用正则表达式了
txt = "China is a great country"
x = re.search("^China.*country$", txt)#如果没有匹配,则返回值 None,而不是 Match 对象。
#Match 对象提供了用于取回有关搜索及结果信息的属性和方法:
#span() 返回的元组包含了匹配的开始和结束位置
#.string 返回传入函数的字符串
#group() 返回匹配的字符串部分
print(x.start(),x.end(),x.group(),x.string)
str = "The rain in Spain falls mainly in the plain!"
#Check if the string contains "ai" followed by 1 or more "x" characters:
x = re.findall("ain+", str)
print(x)
print(re.split("ain+", str))#您可以通过指定 maxsplit 参数来控制出现次数:
print(re.split("ain+", str,maxsplit=1))
print(re.sub("ain+","A", str))#您可以通过指定 count 参数来控制替换次数
print(re.sub("ain+","A", str,count=1))
str = "8 times before 11:45+ AM+"
#Check if the string has any + characters:
print(re.findall("[+]", str))

2、控制流

Python使用缩进来定义代码的块。

python中没有switch

if-elif-else

#Python的if-elif-else用于检查多个表达式,如果条件为True,则执行相应的代码块。
#可以按照下面的方式使用:
#- `if`: 当条件为真(True)时,执行代码块。
x = 1
if x == 1:
    print("x is equal to 1")
#- `if-else`: 用于检查两个条件,如果if条件为真,则执行if代码块,否则执行else代码块。
x = 1
if x == 2:
    print("x is equal to 2")
else:
    print("x is not equal to 2")
#- `if-elif`: 可以有零个或多个elif部分,以及一个可选的else部分。
x = 1
if x < 0:
    print("x is less than 0")
elif x == 0:
    print("x is equal to 0")
else:
    print("x is greater than 0")
#- 嵌套if-elif-else: 可以包含其他if-elif-else语句的if或else代码块。
x = 1
y = 2
if x == y:
    print("x is equal to y")
else:
    if x < y:
        print("x is less than y")
    else:
        print("x is greater than y")

for 

下面是Python for 循环的一些常见写法和使用案例。需要注意的是,or 循环是基于迭代器的,它会在遍历完给定序列的所有元素后自动结束循环。如果需要在循环过程中修改列表或序列,通常要格外小心,或者使用索引访问元素,或者创建原始序列的副本来迭代。

#在Python中,`for` 循环是用来遍历序列(比如列表、元组、字典、集合或字符串)的一种基本循环结构。#下面是Python `for` 循环的一些常见用法:
### 1. 遍历列表或元组
# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)

# 遍历元组
numbers_tuple = (1, 2, 3)
for number in numbers_tuple:
    print(number)
### 2. 遍历字符串

# 遍历字符串中的每个字符
for char in "Hello":
    print(char)
### 3. 使用 `range()` 函数
# 使用内置的 range() 函数
for i in range(5):
    print(i)

# 带有起始和结束值(不包括结束值)
for i in range(1, 6):
    print(i)

# 带有起始、结束值和步长
for i in range(0, 10, 2):
    print(i)
### 4. 遍历字典
# 遍历字典的键
colors = {"red": "#ff0000", "green": "#00ff00", "blue": "#0000ff"}
for color in colors:
    print(color)

# 遍历字典的值
for value in colors.values():
    print(value)

# 遍历字典的键值对
for key, value in colors.items():
    print(key, value)

### 5. 带有 `enumerate()` 函数

# 使用 enumerate() 获取索引及其对应的值
for index, value in enumerate(['a', 'b', 'c']):
    print(index, value)

### 6. 列表推导式
列表推导式提供了一种更简洁的方式来创建列表。

# 使用列表推导式创建一个新列表
squares = [i * i for i in range(10)]
print(squares)

### 7. 遍历集合
# 遍历集合
unique_values = {1, 2, 3, 4, 5}
for value in unique_values:
    print(value)

### 8. 嵌套循环
在一个循环内部使用另一个循环。

# 嵌套循环来访问二维数组元素
matrix = [[1, 2], [3, 4], [5, 6]]
for row in matrix:
    for element in row:
        print(element)

while 

#Python 中的 `while` 循环基本形式是
#while expression:
#    statement(s)
#只要 `expression` 条件为 `True`,`while` 循环就会继续执行,在表达式为 `False` 时退出循环。
#以下是一些使用 `while` 循环的示例:
#1. 基本的 `while` 循环:

    i = 0
    while i < 5:
        print(i)
        i += 1

 #   这个 `while` 循环会反复打印变量 `i` 的值,直到 `i` >= 5 为止。

#2. `while` 循环与 `else` 语句:

    i = 0
    while i < 5:
        print(i)
        i += 1
    else:
        print("Loop has ended")

#    `else` 子句在 `while` 循环表达式变为 `False` 时执行。
#这意味着循环正常结束后,将执行 `else` 子句。

#3. `while` 循环与 `break` 语句:

    i = 0
    while i < 5:
        if i == 3:
            break
        print(i)
        i += 1

#    `break` 语句用于在满足某条件时提前终止循环,如上例中,当 `i` 等于 3 时终止循环。

#4. `while` 循环与 `continue` 语句:

    i = 0
    while i < 5:
        i += 1
        if i == 3:
            continue
        print(i)
#    `continue` 语句用于跳过当前循环的剩余语句,然后继续进行下一轮循环。
#如上例中,当 `i` 等于 3 时,跳过打印语句,直接进行下一轮循环。

3、函数、嵌套函数

在Python中,自定义函数的定义和使用是通过关键字 `def` 来实现的。函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段。函数可以提高应用的模块性和代码的重复利用率。下面是Python自定义函数的基本语法:

slope, intercept, r, p, std_err = stats.linregress(x, y)

返回对象的函数可以用等数量的变量接收对象中的每个参数 

另外,可以正常递归函数

def function_name(parameters):
    """docstring"""
    statement(s)
#以下是这些组成部分的作用:

#1. **function_name**: 函数名,用于唯一标识函数。按照常规,函数名应该是小写的,如果包含多个单词,可以用下划线分隔。
#2. **parameters (参数)**: 参数通过在函数名后的括号内声明。参数是可选的;一个函数可以有任意数量的参数,包括零个。
#3. **docstring**: 可选的文档字符串,用于描述函数的目的和如何使用。虽然是可选的,但强烈推荐添加,以提高代码可读性。
#4. **statement(s)**: 函数体,包含了一系列在函数调用时执行的语句。
#现在,让我们通过几个例子来演示自定义函数的不同写法和用法:

### 1. 无参数函数
def say_hello():
    """Print a greeting text."""
    print("Hello there!")

# 调用函数
say_hello()
#原理:当调用 `say_hello()` 时,会执行函数体内的打印语句。
### 2. 带有参数的函数
def greet(name):
    """Print a personalized greeting."""
    print(f"Hello, {name}!")

# 调用函数
greet("Alice")
#原理:这个函数接受一个参数 `name`,并在调用时需要提供这个参数。
### 3. 带有默认参数的函数
def greet(name, greeting="Hello"):
    """Print a personalized greeting with a default message."""
    print(f"{greeting}, {name}!")
# 调用函数
greet("Alice")            # 使用默认的问候语
greet("Bob", "Goodbye")   # 提供新的问候语
原理:如果调用时没有提供 `greeting` 参数,该函数使用默认值 "Hello"。
### 4. 带有可变数量参数的函数(*args)
def print_args(*args):
    """Print all arguments passed to the function."""
    for arg in args:
        print(arg)

# 调用函数
print_args('one', 'two', 'three')```
#原理:`*args` 允许函数接受任意数量的位置参数。
### 5. 带有关键字参数的函数(**kwargs)
def print_kwargs(**kwargs):
    """Print all keyword arguments passed to the function."""
    for key, value in kwargs.items():
        print(f"{key}: {value}")

# 调用函数
print_kwargs(first='one', second='two', third='three')
#原理:`**kwargs` 允许函数接受任意数量的关键字参数。
### 6. 返回值的函数
def add(a, b):
    """Return the sum of a and b."""
    return a + b
# 调用函数
result = add(2, 3)
print(result)  # 输出: 5
#原理:`return` 语句将函数的结果返回给调用者。
### 7. 匿名(lambda)函数
# 定义 lambda 函数
multiply = lambda x, y: x * y
# 调用 lambda 函数
print(multiply(2, 3))  # 输出: 6
#原理:`lambda` 函数是一个简单的、在一行内定义的匿名函数。

自定义函数可以根据需求编写,它们可以非常灵活,包括处理输入参数、执行操作并返回结果。理解和掌握自定义函数是编程中的一项重要技能,因为它们可以帮助你编写更清晰、更模块化和更可复用的代码。

在Python中,函数内部可以定义其他函数,这些内部定义的函数被称作嵌套函数(nested functions)或局部函数(local functions)。嵌套函数可以访问其外部函数作用域中的变量,但外部作用域不能访问嵌套函数内的变量。这一点符合作用域链的概念,即内部作用域可以访问外部作用域变量,反之则不行。

以下是一个简单的示例,展示了如何在Python中的一个函数内部定义另一个函数:

def outer_function(text):
    # 这是外层函数
    def inner_function():
        # 这是内层嵌套函数
        print(text)
    
    inner_function()  # 调用内层函数

outer_function("Hello, World!")  # 调用外层函数

在这个例子中,`outer_function`是一个外层函数,它接受一个参数`text`。`inner_function`是定义在`outer_function`内部的嵌套函数,它可以访问外层函数的`text`变量,并打印它。当调用`outer_function`时,它随后会调用内部的`inner_function`,进而显示传递给`outer_function`的消息。

lambda 函数

lambda 函数是一种小的匿名函数。

lambda 函数可接受任意数量的参数,但只能有一个表达式。

#一个 lambda 函数,它把作为参数传入的数字加 10,然后打印结果:
x = lambda a : a + 10
print(x(5))
#一个 lambda 函数,它把参数 a、b 和 c 相加并打印结果:
x = lambda a, b, c : a + b + c
print(x(5, 6, 2))
#当您把 lambda 用作另一个函数内的匿名函数时,会更好地展现 lambda 的强大能力。
#如果在短时间内需要匿名函数,请使用 lambda 函数。
#假设您有一个带一个参数的函数定义,并且该参数将乘以未知数字:
#使用该函数定义来创建一个总是使所发送数字加倍的函数:
def myfunc(n):
 return lambda a : a * n
mydoubler = myfunc(2)#返回的是lambda a : a * 2,函数是引用,所以mydoubler变成这个函数了
print(mydoubler(11))
#使用相同的函数定义来创建一个总是使您发送的数字增加三倍的函数
mytripler = myfunc(3)
print(mytripler(11))

4、类

Python中的类(class)是面向对象编程的核心。一个类可以被视为创建对象的蓝图。它定义了用于描述某种类型对象的属性和方法。以下是类的几个关键组成部分:

1. **类定义**:使用关键词`class`来定义一个新类。

2. **类变量**:在类中定义且为所有实例共享的变量。类变量可以通过类本身或其实例来访问。

3. **实例变量**:在类的方法中定义且仅属于类的某个实例的变量。

4. **初始化方法**:`__init__`方法,在创建类的新实例时被调用,用于初始化实例变量。不能存在多个,即不能在类中重载

5. **方法**:定义在类中的函数,用于描述类的行为。

6. **继承**:通过继承可以基于一个类来创建新类,新类继承了原类的属性和行为。

7. **封装**:隐藏对象的内部状态和功能实现细节,仅对外提供接口。

8. **多态性**:不同类的对象可以通过同一接口以不同方式响应相同的请求。

下面是一个包含以上各个组成部分的Python类的完备示例:

当您添加 __init__() 函数时,子类将不再继承父的 __init__() 函数。

注释:子的 __init__() 函数会覆盖对父的 __init__() 函数的继承。

如需保持父的 __init__() 函数的继承,请添加对父的 __init__() 函数的调用:

父类名或super().__init__
class Animal:
    # 类变量
    kingdom = 'Animalia'
    
    def __init__(self, species, age):
        # 实例变量
        self.species = species
        self.age = age
    
    # 方法示例
    def describe(self):
        print(f"This is a {self.species}, aged {self.age}.")
        
    # 类静态方法
    @staticmethod
    def get_kingdom():
        return Animal.kingdom

# 继承创建新类
class Dog(Animal):
    def __init__(self, name, age):
        # 调用父类的初始化方法
        super().__init__('Dog', age)
        self.name = name  # 新的实例变量
    
    def bark(self):
        print("Woof!")

# 创建Animal类的实例
animal = Animal('Penguin', 5)
animal.describe()
print(animal.get_kingdom())  # 调用静态方法

# 创建Dog类的实例
dog = Dog('Buddy', 3)
dog.describe()
dog.bark()

# 类变量也可以通过类名直接访问
print(Dog.kingdom)

这段代码首先定义了一个`Animal`类,它有一个类变量`kingdom`和一些实例变量。接着,我们定义了一个`Dog`类,它继承自`Animal`类并添加了新的方法和实例变量。然后我们创建了这两个类的实例并调用了它们的方法来演示封装、继承和多态性。

为了展示以上提到的面向对象编程的概念,我会创建一个网络爬虫相关的例子。以下是一个简单的网络爬虫类的定义,涵盖了类定义、类变量、实例变量、初始化方法、方法以及继承的特点。此外,我也会示范封装和多态性。

# 导入所需库
import requests
from bs4 import BeautifulSoup

# 基础爬虫类
class BaseCrawler:
    user_agent = 'BaseCrawler/1.0'  # 类变量,所有实例共享
    
    def __init__(self, base_url):
        self.base_url = base_url  # 实例变量,每个实例独有
        self.headers = {'User-Agent': BaseCrawler.user_agent}
    
    def fetch_page(self, path=''):
        response = requests.get(self.base_url + path, headers=self.headers)
        return response
    
    def parse_page(self, html):
        # 由子类实现具体的解析方法
        raise NotImplementedError

# 特定网站的爬虫类,继承自BaseCrawler
class MyWebsiteCrawler(BaseCrawler):
    
    # 重写初始化方法,可以添加特定参数
    def __init__(self, base_url, extra_param):
        super().__init__(base_url)  # 调用父类初始化方法
        self.extra_param = extra_param
    
    # 实现解析页面的方法,展示多态性
    def parse_page(self, html):
        soup = BeautifulSoup(html, 'html.parser')
        data = soup.find_all(class_='target-class')  # 假设我们需要抓取具有特定class的元素
        return data
    
# 封装:客户端代码只需要创建对象和调用方法,不需了解内部实现
if __name__ == '__main__':
    # 创建MyWebsiteCrawler的实例
    crawler = MyWebsiteCrawler('https://example.com', 'extra')
    page_content = crawler.fetch_page()  # 抓取页面
    parsed_data = crawler.parse_page(page_content.text)  # 解析页面
    print(parsed_data)  # 输出解析后的数据

这个例子中,我们首先定义了一个基础的爬虫类`BaseCrawler`,它包含用来发送网络请求的`fetch_page`方法和一个抽象的`parse_page`方法,后者需要在子类中具体实现。`BaseCrawler`类中的`user_agent`是一个类变量,被所有实例共享。

我们接着定义了`MyWebsiteCrawler`类,它继承自`BaseCrawler`类。在这个子类中,我们添加了一个实例变量`extra_param`,并重写了`parse_page`方法来适应特定网站的解析逻辑,展示了多态性——即同一个方法在不同的子类中有不同的实现方式。

在实际运行爬虫的时候,客户端代码(`if __name__ == '__main__':`部分)不需要了解`MyWebsiteCrawler`内部是如何实现页面抓取和解析的,体现了封装的原则。客户端代码只关心抓取和解析的结果。这使得我们可以在不影响客户端代码的前提下更改或扩展爬虫类的内部实现。

完备演示


# 导入必要的库
from abc import ABC, abstractmethod

# 定义一个基类
class Base(ABC):
    # 构造函数
    def __init__(self, value):
        # 普通属性
        self.value = value
        print(f"Base constructor called with value: {value}")

    # 析构函数
    def __del__(self):
        print(f"Base destructor called for value: {self.value}")
        
    # 抽象成员函数,定义在抽象类中,子类必须实现
    @abstractmethod
    def do_something(self):
        pass
    
    # 静态函数
    @staticmethod
    def static_method():
        print("Base static_method called.")
        
    # 类方法
    @classmethod
    def class_method(cls):
        print(f"Base class_method called for class {cls}")

    # 特殊方法示例:重载 '+' 运算符
    def __add__(self, other):
        if isinstance(other, Base):
            return self.value + other.value
        else:
            raise ValueError("Can only add Base instances.")
    
    # ... 省略其他所有运算符的特殊方法重载,完整实现将非常冗长
    # 例如,重载比较运算符:
    def __eq__(self, other):
        return self.value == other.value
        
    # 私有属性(通过名称改编)
    __private_attribute = "I am private"
    
    # 保护属性(通过约定),以单一下划线开头
    _protected_attribute = "I am protected"
    
    # 普通函数
    def print_value(self):
        print(self.value)

# 实现抽象类
class Concrete(Base):
    def __init__(self, value, extra):
        super().__init__(value)
        self.extra = extra
    
    def do_something(self):
        print("Concrete do_something called.")
    
    def access_private_attribute(self):
        # 正确的私有属性访问方式,但仍然会因为内部的名称改编而不成功
        try:
            print(self.__private_attribute)  
        except AttributeError as e:
            print(e)
            # 正确访问私有变量的方法
            print(self._Base__private_attribute)

# 单继承展示
class Subclass(Concrete):
    def __init__(self, value, extra, sub_extra):
        super().__init__(value, extra)
        self.sub_extra = sub_extra

# 多重继承展示
class AnotherBase:
    def another_method(self):
        print("AnotherBase another_method called.")

class MultiDerived(Concrete, AnotherBase):
    def __init__(self, value, extra):
        Concrete.__init__(self, value, extra)  # 显式调用

# 私有继承展示——实际上是将父类作为私有子类属性
class PrivateInheritance:
    def __init__(self, base_instance):
        # 私有属性
        self.__base = base_instance
    
    def use_base_methods(self):
        self.__base.print_value()
        

# 现在,我们创建 Concrete 的实例和使用它
concrete_instance = Concrete(10, "extra_info")
concrete_instance.do_something()
concrete_instance.print_value()

# 静态和类方法调用
Base.static_method()
Base.class_method()

# 运算符重载调用
another_instance = Concrete(20, "another_extra")
print(f"Sum of values: {concrete_instance + another_instance}")

# 访问私有和保护属性
# concrete_instance.__private_attribute  # 将抛出错误,因为属性是私有的
concrete_instance.access_private_attribute()  # 通过函数访问
print(concrete_instance._protected_attribute)  # 可以直接访问,但不推荐

# 单继承和多重继承的实例
subclass_instance = Subclass(30, "subclass_extra", "sub_extra")
subclass_instance.print_value()

multi_derived_instance = MultiDerived(40, "multi_extra")
multi_derived_instance.another_method()

# 私有继承的示例
private_inheritance_instance = PrivateInheritance(concrete_instance)
private_inheritance_instance.use_base_methods()

# 注意:由于Python的动态性,私有和保护属性并不会像在某些静态语言中那样被强制限制访问,而是依赖于约定和名称改编。

运算符重载 

不可重载的运算符列表:

  1. is
  2. not
  3. and
  4. or
  5. lambda

可重载的运算符的函数名列表:

- `__add__(self, other)`:加法运算符(`+`)
- `__sub__(self, other)`:减法运算符(`-`)
- `__mul__(self, other)`:乘法运算符(`*`)
- `__truediv__(self, other)`:真除法运算符(`/`)
- `__floordiv__(self, other)`:整除法运算符(`//`)
- `__mod__(self, other)`:取模运算符(`%`)
- `__divmod__(self, other)`:商和模运算符(`divmod()`)
- `__pow__(self, other[, modulo])`:指数运算符(`**`)
- `__lshift__(self, other)`:左位移运算符(`<<`)
- `__rshift__(self, other)`:右位移运算符(`>>`)
- `__and__(self, other)`:按位与运算符(`&`)
- `__xor__(self, other)`:按位异或运算符(`^`)
- `__or__(self, other)`:按位或运算符(`|`)

对于比较运算符:

- `__lt__(self, other)`:小于运算符(`<`)
- `__le__(self, other)`:小于等于运算符(`<=`)
- `__eq__(self, other)`:等于运算符(`==`)
- `__ne__(self, other)`:不等于运算符(`!=`)
- `__gt__(self, other)`:大于运算符(`>`)
- `__ge__(self, other)`:大于等于运算符(`>=`)

赋值运算符的增强版本:

- `__iadd__(self, other)`:加法赋值运算符(`+=`)
- `__isub__(self, other)`:减法赋值运算符(`-=`)
- `__imul__(self, other)`:乘法赋值运算符(`*=`)
- `__itruediv__(self, other)`:真除法赋值运算符(`/=`)
- `__ifloordiv__(self, other)`:整除法赋值运算符(`//=`)
- `__imod__(self, other)`:取模赋值运算符(`%=`)
- `__ipow__(self, other[, modulo])`:指数赋值运算符(`**=`)
- `__ilshift__(self, other)`:左位移赋值运算符(`<<=`)
- `__irshift__(self, other)`:右位移赋值运算符(`>>=`)
- `__iand__(self, other)`:按位与赋值运算符(`&=`)
- `__ixor__(self, other)`:按位异或赋值运算符(`^=`)
- `__ior__(self, other)`:按位或赋值运算符(`|=`)

一元运算符和其他内置函数重载:

- `__pos__(self)`:正号(`+`)
- `__neg__(self)`:负号(`-`)
- `__abs__(self)`:绝对值(`abs()`)
- `__invert__(self)`:按位非运算符(`~`)
- `__complex__(self)`:复数转换(`complex()`)
- `__int__(self)`:整数转换(`int()`)
- `__float__(self)`:浮点数转换(`float()`)
- `__round__(self[, ndigits])`:四舍五入(`round()`)
- `__index__(self)`:转换为索引时(如用在切片操作中)

容器类型运算符重载:

- `__len__(self)`:对象长度(`len()`)
- `__getitem__(self, key)`:获取元素(`[]`)
- `__setitem__(self, key, value)`:设置元素(`[]`)
- `__delitem__(self, key)`:删除元素(`del []`)
- `__iter__(self)`:返回迭代器(`iter()`)
- `__reversed__(self)`:倒序迭代(`reversed()`)
- `__contains__(self, item)`:成员运算符(`in`)

import numpy as np

class Matrix:
    def __init__(self, matrix):
        # 确保输入是一个二维数组。
        self.matrix = np.array(matrix)
    def __str__(self):
        # 输出矩阵的字符串表示。
        return str(self.matrix)

    # 算术运算符
    def __add__(self, other):
        # 矩阵加法
        return Matrix(self.matrix + other.matrix)

    def __sub__(self, other):
        # 矩阵减法
        return Matrix(self.matrix - other.matrix)

    def __mul__(self, other):
        # 矩阵乘法
        if isinstance(other, Matrix):
            return Matrix(self.matrix @ other.matrix)  # 矩阵乘法
        else:
            return Matrix(self.matrix * other)  # 数量乘法
    # 矩阵乘法
    def __matmul__(self, other):
        return Matrix(self.matrix @ other.matrix)
    # 矩阵的幂运算
    def __pow__(self, power, modulo=None):
        return Matrix(np.linalg.matrix_power(self.matrix, power))

    def __truediv__(self, other):
        # 矩阵除法(这通常没有定义,但我们这里做一个示例,使用逆矩阵)
        return Matrix(np.linalg.inv(self.matrix) @ other.matrix)
    def __iadd__(self, other):
        # += 运算符重载
        if isinstance(other, Matrix):
            self.matrix += other.matrix
        else:
            self.matrix += other
        return self

    def __isub__(self, other):
        # -= 运算符重载
        if isinstance(other, Matrix):
            self.matrix -= other.matrix
        else:
            self.matrix -= other
        return self

    def __imul__(self, other):
        # *= 运算符重载
        if isinstance(other, Matrix):
            self.matrix = np.dot(self.matrix, other.matrix)
        else:
            self.matrix *= other
        return self

    def __itruediv__(self, other):
        # /= 运算符重载
        if isinstance(other, Matrix):
            self.matrix =self.__truediv__(other)
        else:
            raise NotImplementedError("Matrix division is not well-defined.")
        return self

    # 反向运算符
    def __radd__(self, other):
        # 反向加法
        return self.__add__(other)
    # 矩阵取反
    def __neg__(self):
        return Matrix(-self.matrix)

    # 返回矩阵的转置
    def __getitem__(self, key):
        if isinstance(key, str) and key == "T":
            return Matrix(self.matrix.T)
        else:
            raise KeyError("Invalid key to access Matrix elements.")
    '''
    请注意,本例中的 __getitem__ 方法只是为了说明如何重载索引运算符,并不是实际推荐的方式来获取矩阵的转置。
    通常,转置操作应该定义为一个方法,例如 transpose,或者利用现有的 numpy.ndarray.T 属性。
    在实际应用中,矩阵类应该提供更加完整的索引和切片操作,可以通过丰富 __getitem__ 和 __setitem__ 方法来实现。
    '''

    # 比较运算符
    def __eq__(self, other):
        # 检查矩阵是否相等
        return np.array_equal(self.matrix, other.matrix)
    #矩阵的不等号运算 (__ne__),比较两个矩阵是否不等
    #__lt__ (小于), __gt__ (大于), __le__ (小于等于), 和 __ge__ (大于等于)
    #_sum 方法用于计算矩阵中所有元素的总和,然后我们在特殊方法中使用这个值来进行比较。这只是一种实现方式,具体取决于你希望如何比较矩阵
    def __ne__(self, other):
        return not self.__eq__(other)
    def __lt__(self, other):
        return self._sum() < other._sum()

    def __gt__(self, other):
        return self._sum() > other._sum()

    def __le__(self, other):
        return self._sum() <= other._sum()

    def __ge__(self, other):
        return self._sum() >= other._sum()

    def _sum(self):
        a=sum(sum(row) for row in self.matrix)
        return a
    def __mod__(self, other):
        # % 运算符重载
        if isinstance(other, Matrix):
            print("j1")
            ma =np.mod(self.matrix, other.matrix)
            return ma
        else:
            print("jin2")
            ma = np.mod(self.matrix, other)
        return ma

# 示例
mat1 = Matrix([[1, 2], [3, 4]])
mat2 = Matrix([[5, 6], [7, 8]])

# 算术运算符调用示例
print(mat1 + mat2)  # 输出矩阵和
print(mat1 - mat2)  # 输出矩阵差
print(mat1 * mat2)  # 输出矩阵积
print("\n矩阵的幂运算:")
print(mat1 ** 2)
print("矩阵乘法:")
print(mat1 @ mat2)
print("\n矩阵取反:")
print(-mat1)
print("\n矩阵的转置:")
print(mat1['T'])
print(mat1 < mat2)  # True
print(mat1 > mat2)  # False
print(mat1 <= mat2) # True
print(mat1 >= mat2) # False
# ...

# 比较运算符调用示例
print(mat1 == mat2)  # 检查矩阵是否相等
print(mat1!=mat2)
mat1+=mat2
print("+= ",mat1)
mat1-=mat2
print("-= ",mat1)
mat1*=mat2
print("*= ",mat1)
mat1/=mat2
print("/= ",mat1)
print("%",mat1%mat2)
# ...

# 矩阵类将逐渐完善,包括错误处理、特殊场景处理等。

5、多态

在Python中,多态(即java、c++中的动态多态)是一种在不同对象类型间使用共同的接口的能力。这意味着函数可以使用任何对象,只要该对象支持预期的方法或行为。多态的实现不需要复杂的类型系统;Python这种动态类型语言会在运行时决定方法的调用,而不是在编译时。

以下是Python中实现多态的一个简单例子:


class Bird:
    def fly(self):
        print("Some birds can fly")

class Sparrow(Bird):
    def fly(self):
        print("Sparrow flies")

class Ostrich(Bird):
    def fly(self):
        print("Ostrich cannot fly")

# 多态用法
def bird_fly(bird):
    bird.fly()

# 创建鸟类实例
sparrow = Sparrow()
ostrich = Ostrich()

# 通过通用接口调用不同实例的方法
bird_fly(sparrow)  # 输出: Sparrow flies
bird_fly(ostrich)  # 输出: Ostrich cannot fly

在这个例子中,我们定义了一个基类`Bird`和两个子类`Sparrow`和`Ostrich`,这两个子类都覆写了基类中的`fly`方法。尽管它们具有相同的接口(都有`fly`方法),但具体实现不同。

函数`bird_fly`接受任何类型的`bird`对象,并调用它的`fly`方法。当我们传入`Sparrow`实例和`Ostrich`实例时,它们以不同的方式响应相同的接口;这就是多态性。由于Python是动态类型语言,它不要求这些对象是相同的数据类型,只要它们实现了被调用的方法即可。

多态主要带来以下两个优点:
1. **灵活性**:允许不同的对象对同一消息作出响应。
2. **可互换性**:可以在不同对象之间互换使用,只要它们符合期望的接口。

在Python中实现多态非常自然,这得力于语言的灵活性和对鸭子类型的支持(如果它像鸭子,叫声像鸭子,那么它就可以当作鸭子)。这使得Python中的多态实现通常是隐式的,而不需要通过严格的类型继承关系来实现。

6、异常处理

在Python中,try-catch机制实际上是利用了`try`和`except`语句块来处理异常。当你期望某段代码可能会出现错误时,你可以将它放在`try`块中。如果在`try`块内的代码执行过程中发生了异常,Python将停止当前的执行过程,转而跳到`except`块处理这个异常。

以下是Python中使用`try`和`except`的基本语法:

try:
 x = "hello"
 print(x)
 if not type(x) is int:
  raise TypeError("Only integers are allowed")
 #raise 关键字用于引发异常。您能够定义所引发异常的类型、以及打印给用户的文本。
except NameError:
 print("Variable x is not defined")
except Exception as a:#捕获异常对象
 print("Something else went wrong"," ",a)
else:#如果没有引发错误,那么您可以使用 else 关键字来定义要执行的代码块
 print("Nothing went wrong")
finally:#如果没有引发错误else和finally都执行,无论 try 块是否引发错误,都会执行 finally 块
 print("The 'try except' is finished")

try:
    # 尝试执行的代码
    # 可能会引发异常的操作
except SomeException as e:
    # 捕获到的异常处理代码
    # SomeException是具体的异常类型
    print("An error occurred:", e)

#你还可以捕获多个异常:


try:
    # 尝试执行的代码
except FirstException:
    # 针对FirstException的处理
except SecondException:
    # 针对SecondException的处理


#另外,还有`else`和`finally`语句块可用。`else`块会在没有异常发生时执行,而`finally`块无论是否##发生异常都会执行,常用于执行清理操作,比如关闭文件:


try:
    # 尝试执行的代码
except SomeException:
    # 异常发生时的处理
else:
    # 如果没有异常发生则执行此处代码
finally:
    # 不管有没有异常发生都会执行此处代码
    # 常用于清理资源,如关闭文件


#在文件操作的上下文中,`try`和`except`通常被用来确保文件即使在遇到错误时也能被正确关闭。例如:


try:
    f = open('file.txt', 'r')
    data = f.read()
    # 对文件进行一些处理
except IOError as e:
    print("An IOError occurred:", e)
finally:
    f.close()

#不过,在文件操作中,通常更推荐使用`with`语句,因为它会自动帮你关闭文件,即使在文件操作过程中发生了异常。


with open('file.txt', 'r') as f:
    data = f.read()
    # 对文件进行一些处理
    # 文件会在这个代码块结束后自动关闭,即使发生了异常

这样做更简洁,也更安全。

7、IO流

打开文件open(name, mode=None, buffering=None),其中name是文件名

buffering:这是可选参数,用于设置文件缓冲。如果设置为0,则关闭缓冲;如果为1,则行缓冲;如果大于1,则使用指定的缓冲区大小;如果为负,系统使用默认的缓冲策略。

操作码(即open函数的mode)

  • 'r': 以读取模式打开文件,这是默认模式。如果文件不存在,抛出错误。

  • 'w': 以写入模式打开文件。如果文件已存在,其内容会被清空。如果文件不存在,创建新文件。

  • 'a': 以追加模式打开文件。如果文件已存在,写入的数据会被添加到文件末尾。如果文件不存在,创建新文件。

  • 'x': 以创建并写入模式打开文件,如果文件存在则返回错误。

  • 'b': 以二进制模式打开文件。通常与其他模式一起使用,如"rb","wb","ab","xb"。

  • 't': 以文本模式打开文件。默认值,通常与其他模式一起使用。

  • '+': 打开一个文件进行更新(读取和写入)。可以和任何其他模式一起使用。

  1. 若要在处理文件时自动处理文件关闭,可以使用 with 关键字,它可以在完成对文件的处理后自动关闭文件。
  2. 对于二进制文件,你需要在模式字符串中包含 'b'。
  3. 如果你在打开文件时没有指定模式,那么默认模式将为 'rt'(读取文本)。
  4. 'r+', 'w+' 和 'a+' 这三种模式都允许进行读写操作,但 'r+' 不会自动创建新文件,而 'w+' 和 'a+' 会自动创建文件。
  5. 'w' 和 'w+' 会在打开文件时清空文件内容。

 函数

  1. file.read(size=-1):此函数从文件中读取`size`个字节的内容返回。如果`size`参数未指定或指定为负数,则读取并返回文件中的所有剩余内容。
  2. file.readline(size=-1):此函数读取当前行,从当前位置开始到行尾,包含行尾的换行符。如果文件末尾已到,则返回一个空字符串。如果给定的大小为负,则读取剩余的所有字符。
  3. file.readlines(hint=-1): 如果 hint 为负或未给定则读取所有行,读取文件的所有行,并将它们作为字符串列表返回。这个列表的每个元素都是文件中的一行。如果给定了 hint 则根据提示行数来返回。
  4. file.write(s):这个函数将一个字符串写入到当前文件。这个函数将返回实际写入的字符数。不会自动添加行尾的换行符,所以你需要在需要换行的地方自己包含`\n`。
  5. file.writelines(lines):此函数将一个元素lines为字符串的列表写入文件,写入项之间不添加任何分隔符。
  6. file.seek(offset, whence=0):该功能用于更改当前文件的位置。 Offset 表示要移动的字节数。 Whence 值为 0(默认)表示文件开头,1 表示相对于当前位置,2表示文件末尾。从whence指定的位置移动offset个单位长度
  7. file.tell():此函数返回当前文件读取/写入的位置,该位置从文件开头以字节为单位计算。
  8. file.close():当完成所有文件操作后,该函数会关闭文件,释放系统的资源。简单的说,它会刷新任何还没有写入的信息,并关闭文件对象,之后就不能再进行写入或读取操作。
  9. file.flush(): 刷新文件的内部缓冲区,直接将缓冲区里的数据立即写入文件,同时清空缓冲区,但不关闭文件。
  10. 如需删除文件,必须导入 OS 模块,并运行其 os.remove() 函数,为避免出现错误,您可能需要在尝试删除文件之前检查该文件是否存在
    import os
    if os.path.exists("demofile.txt"):
      os.remove("demofile.txt")
    else:
      print("The file does not exist")
  11. 如需删除整个文件夹,请使用 os.rmdir() 方法,您只能删除空文件夹,os.rmdir("myfolder")

struct模块

struct.calcsize

`struct.calcsize` 函数用于计算格式字符串所定义的结构体的大小,即以字节为单位的大小。这个大小是根据给定的格式字符串,按照C语言的数据类型对应到Python数据类型进行算出的。

下面是一些常用的格式字符及它们对应的C类型和Python类型:

- `'b'`:表示 signed char,对应 Python 中的 int 类型。
- `'B'`:表示 unsigned char,对应 Python 中的 int 类型。
- `'h'`:表示 signed short,对应 Python 中的 int 类型。
- `'H'`:表示 unsigned short,对应 Python 中的 int 类型。
- `'i'`和`'l'`:分别表示 signed int 和 long,对应 Python 中的 int 类型。
- `'I'`和`'L'`:分别表示 unsigned int 和 long,对应 Python 中的 int 类型。
- `'q'`:表示 signed long long,对应 Python 中的 int 类型。
- `'Q'`:表示 unsigned long long,对应 Python 中的 int 类型。
- `'f'`:表示 float,对应 Python 中的 float 类型。
- `'d'`:表示 double,对应 Python 中的 float 类型。
- `'c'`:表示 char,对应 Python 中的 bytes 类型,长度为1。
- `'s'`:表示 char[],对应 Python 中的 bytes 类型。
- `'p'`:表示 char[],对应 Python 中的 bytes 类型,第一个字节表示字符串长度。
- `'P'`:表示 void *,即一个指针,对应 Python 中的 int 类型或 long 类型,取决于系统。

每个格式前可以有一个数字表示数量,比如`'4h'`表示四个连续的signed short值。

下面是使用`struct.calcsize`的几个示例:


import struct

# 单个字符
print(struct.calcsize('c'))  # 通常是 1

# 4个整数
print(struct.calcsize('4i'))  # 通常是 16

# 两个字符加上一个整数(注意对齐)
print(struct.calcsize('2ci'))  # 可能是 6,但也可能是 8 或更多,因为结构体可能需要对齐

# 浮点数和双精度数
print(struct.calcsize('fd'))  # 一般来说 float=4, double=8, 所以是 12

# 带有次序的结构体
print(struct.calcsize('!i'))  # 使用网络字节次序的整数,通常是 4

# NULL结束的字符串
print(struct.calcsize('p'))  # 字符串前缀长度的 C风格的字符串

# 混合类型
print(struct.calcsize('hhl'))  # 结合两个短整型和一个长整型

请注意,结构体的实际大小可能会因为对齐(padding)而不同。根据不同平台和编译器,对齐可能会对结构体的大小有较大影响。例如,如某些系统可能要求特定类型的数据要在内存中按特定的字节对齐。

使用`struct.calcsize`时,还可以指定字节顺序,对于跨平台的二进制数据操作而言这一点非常重要。字节顺序前缀有以下几种:

- `'@'`:默认顺序
- `'='`:原生顺序
- `'<'`:小端顺序
- `'>'`或`'!'`:大端顺序

这些字节顺序前缀可以用来确保数据的二进制表示在不同架构的机器之间是一致的。

演示不同字节顺序的大小:


# 默认字节顺序
print(struct.calcsize('@i'))  # 本地机器的默认设置

# 小端字节顺序
print(struct.calcsize('<i'))  # 通常是 4

# 大端字节顺序
print(struct.calcsize('>i'))  # 也通常是 4

struct.pack

`struct.pack` 函数是 Python 中 `struct` 模块的组成部分,它根据指定的格式 (`format string`) 将 Python 数据转换成字符串(在 Python 2)或字节序列(在 Python 3),这可以用于存储或传输数据。下面是 `struct.pack` 的基本用法和它支持的数据类型的介绍。

### struct.pack 基础用法

#`struct.pack` 的语法如下:
python
struct.pack(fmt, v1, v2, ...)

- `fmt` 是一个格式字符串,用于指定预期的布局。
- `v1, v2, ...` 是要打包的值。

格式字符串 `fmt` 的第一个字符可以是一个可选的字节顺序、大小和对齐方式,通常称为 "字节顺序标记":

- `<`:小端字节序
- `>` 或 `!`:大端字节序
- `@`:本机
- `=`:本机标准
- `^`:本机最小对齐(几乎等同于 `@`)

其后跟上表示特定二进制数据类型的字符,如:

- `x`:pad byte(无数据只填充)
- `b`:signed char
- `B`:unsigned char
- `?`:_Bool
- `h`:signed short
- `H`:unsigned short
- `i` 或 `l`:signed int
- `I` 或 `L`:unsigned int or unsigned long
- `q`:signed long long
- `Q`:unsigned long long
- `f`:float
- `d`:double
- `s`:char[](bytes/array of chars)
- `p`:pascal string(length byte + string)
- `P`:pointer(根据平台可能是32或64位)

每种类型还可以有一个前缀数字,表示数量:

- `2s` 表示长度为2的字符串。

以下是一些 `struct.pack` 的示例:

import struct

# 打包一个无符号短整数和一个浮点数,小端字节序
packed_data = struct.pack('<Hf', 12345, 67.89)

# 打包两个整数(一个短整数和一个长整数),大端序
packed_data = struct.pack('>hl', -123, 45678)

# 打包一个布尔值和三个字符
packed_data = struct.pack('<?3s', True, b'abc')

# 打包四个字节,每个字节代表一个无符号整数
packed_data = struct.pack('4B', 192, 168, 1, 1)

使用 `struct.pack` 演示时要注意 Python 版本的差异,Python 3 中 `struct.pack` 返回的是字节序列,而 Python 2 中返回字符串。在操作二进制数据时通常使用 Python 3,因为它对二进制数据的处理更加清晰和一致。

请记住,`struct.pack` 用于将Python数据类型转换为二进制表示,经常用于紧凑的二进制通信协议或文件格式,并且需要匹配所使用平台的字节序和数据表示。

struct.pack_into

`struct.pack_into` 函数是Python标准库 `struct` 模块中的一个函数,用于将数据打包进一个存在的 `bytearray` 或者可变的缓冲区中,而不是创建一个新的 `bytes` 对象。`pack_into` 的存在可以减少内存分配和复制的次数,这在处理大量数据或者对性能有特别要求的应用中非常有用。

函数原型为:

struct.pack_into(fmt, buffer, offset, v1, v2, ...)


- `fmt`:格式字符串,用于指定打包数据的格式。
- `buffer`:接收数据的 `bytearray` 或 `memoryview` 等支持缓冲区协议的对象。
- `offset`:偏移量(以字节为单位),表示从缓冲区的开始位置到打包数据开始位置的距离。
- `v1, v2, ...`:要打包的值。

我们将通过一个例子来演示 `struct.pack_into` 的用法:


import struct

# 我们将用以下格式来打包数据:一个整数和一个浮点数
# 'i' 表示一个4字节整数,'f' 表示一个4字节浮点数
fmt = 'if'

# 创建一个足够大的缓冲区存储打包后的数据
buffer = bytearray(struct.calcsize(fmt))

# 打包的数据
values = (12345, 67.89)

# 打包数据到缓冲区,从索引5开始打包
struct.pack_into(fmt, buffer, 5, *values)

# 查看缓冲区内容
print(list(buffer)) # 输出字节序列列表

这段代码使用 `struct.pack_into` 将一个整数和一个浮点数打包到一个已经存在的 `bytearray` 中。我们这里假设这个 `bytearray` 足够大,可以在索引5的位置开始存储打包后的数据。打包完成后,我们输出 `bytearray` 的内容查看结果。

重要的是在实际使用 `struct.pack_into` 时遵循以下几个要点:
- 缓冲区的大小至少要与`fmt`格式字符串所定义的数据大小加上`offset`一致。
- 小心处理内存对齐,不同的硬件架构和操作系统可能会有不一样的对齐要求。
- 通过明智地选择`offset`值,我们可以在缓冲区中组织数据,但必须确保不能覆盖重要的数据。

请记住,与 `struct.pack` 不同,`struct.pack_into` 不会返回新的字节对象,而是直接修改传入的缓冲区。

struct.unpack

在以上演示中,整型(`'i'`)不论是小端还是大端,大小通常是一样的,但是对于更复杂的或者更大的结构体,不同的字节顺序规则可能会导致结构体的最终大小不同。

Python中的`struct`模块提供了打包和解包二进制数据的工具。这是一个非常有用的工具,特别是当你处理网络数据或其他类型的二进制流时。

`struct.unpack`函数用于将打包的二进制数据转换回原始的值。它接受两个参数,第一个参数是格式字符串,它指定了数据应该如何解析,第二个参数是要解析的二进制数据。

格式字符串是由一系列的类型指标构成的。一些常见的类型指标包括:

- `s` : 字符串
- `h` : 短整数
- `i` : 整数
- `l` : 长整数
- `f` : 浮点数
- `d` : 双精度浮点数

你还可以在类型指标前面加上数量,例如`2s`表示两个字符串。

下面是一个基本示例:


import struct

data_to_unpack = struct.pack("ihbf", 6, -3422, 5.728, 1923.344634)

print(struct.unpack("ihbf", data_to_unpack))


'''输出结果为:


(6, -3422, 5.727999687194824, 1923.3446350097656)
'''

这个例子中,我们首先使用`struct.pack`将一个整数、一个短整数、一个单精度浮点数和一个双精度浮点数打包成了一串二进制数据。然后我们使用`struct.unpack`将这些数据解包回原始的值。注意输出中的浮点数值可能由于精度问题略有不同。

你还可以在格式字符串中使用`<`和`>`来指定字节顺序。例如,`<i`表示一个小端字节序的整数,而`>i`表示一个大端字节序的整数。默认的字节序是由你的机器决定的,如果你在一个小端字节序的机器上运行程序,那么`i`就表示一个小端字节序的整数。

如果你要解包的数据有不同的类型,你就需要更复杂的格式字符串。例如,如果你要解包一个包含一个整数和两个浮点数的数据,你就需要使用格式字符串`ifd`。

另外,对于Python 2.x,你需要为长整数添加"L"后缀,例如,`l`就表示一个长整数。对于Python 3.x,你不需要添加任何后缀,`l`就可以表示一个长整数。

请注意,`unpack`函数返回的是一个元组,你可以使用索引来访问特定的值。

这是一个基本的`struct.unpack`的用法介绍,希望能对你有所帮助。

struct.unpack_from

`struct.unpack_from()`函数是Python的struct模块中的一个方法,这个函数可以按照给定的格式字符串(format),从指定的buffer中的指定位置(offset)开始,解析出原始数据。

函数原型如下:


struct.unpack_from(format, buffer, offset=0)


参数说明:
- **format**:指定数据类型的格式字符串。例如,'ih'表示一个int类型和一个short类型。
- **buffer**:包含有需要解拆的数据的缓冲区。这个缓冲区可以是任何实现了缓冲接口的对象或者是包含字节的可迭代对象,例如:bytes,bytearray等
- **offset**:一个整数,指定在缓冲区中从何处开始解包。它的默认值为0,这表示从缓冲区的开始位置开始解包。下面是一个`struct.unpack_from()`的使用示例:


import struct

# Create a bytes object
buffer = struct.pack('ihb', 7, 8, 97)
print("buffer:", buffer)

# Using 'struct.unpack_from()' method to decode the packed data.
# 'ihb' stands for int, short and char, respectively.
result = struct.unpack_from('ihb', buffer)

print("Result:", result)

struct.iter_unpack

在这个例子中,我们首先创建一个包含一些打包的数据的缓冲区。然后,我们使用`struct.unpack_from()`函数从缓冲区中解包出这些数据。在这个过程中,我们使用了'ihb'作为格式字符串,它表示我们要解包出一个int,一个short,和一个char。

`struct.iter_unpack`是Python的`struct`模块中的一个函数,它是一种用来解包二进制数据的方法。这个函数可以解包二进制字符串,并返回一个迭代器,迭代器的每一项都是一个解包的元素。

其基本格式如下:


iterator = struct.iter_unpack(format, buffer)


其中,`format`是一个字符串,用来指定待解包数据的类型;`buffer`是包含有待解包数据的二进制字符串。

让我们通过一个例子来说明`struct.iter_unpack`的用法:


import struct

# 我们有一个包含了四个int类型数据的二进制字符串
data = struct.pack('4I', 1, 2, 3, 4)

# 现在我们可以使用iter_unpack函数来解包数据
iterator = struct.iter_unpack('I', data)

# 迭代并打印每一个解包的元素
for item in iterator:
    print(item)

'''
当你执行上述代码时,你会看到输出:

(1,)
(2,)
(3,)
(4,)
'''


注意每一个解包的元素都是一个tuple类型的,虽然它们只包含一个元素。

这就是`struct.iter_unpack`的基本用法。需要注意的是,`format`和`buffer`中的元素数量必须匹配,否则Python会抛出一个`struct.error`错误。

IO模块

BufferedRandom

`BufferedRandom` 是 Python 的 `io` 模块中定义的一个类,它提供了对随机访问流的缓冲接口。这意味着你可以在文件中向前或向后移动,读取或者写入数据。以下是该类方法的一般用法和解释:

1. `close()`: 关闭流,释放资源。

2. `detach()`: 分离底层的二进制缓冲区(raw stream),并返回它。

3. `fileno()`: 返回底层文件描述符(如果有)。

4. `flush()`: 刷清缓冲区的内容,确保所有暂存在缓冲区的数据被写入底层设备。

5. `isatty()`: 如果流是一个交互式的 tty 设备,则返回 True。

6. `peek(size)`: 返回文件的下一部分内容而不移动文件指针,但不保证返回特定的字节数。

7. `read(size)`: 读取并返回文件中最多 `size` 个字节的内容,如果没有指定 `size`,则读取直到文件末尾。

8. `read1(size)`: 读取并返回文件中最多 `size` 个字节的内容,但在单个底层 raw read 中不会读取多于 read buffer size 的字节数。

9. `readable()`: 如果流可以读取,则返回 True。

10. `readinto(b)`: 将字节读入预先分配的、可变的字节数组 `b` 中,返回读取的字节数。

11. `readinto1(b)`: 类似于 `readinto()`,但只在单个底层 raw read 调用中读取。

12. `readline(limit)`: 读取直到遇到换行符的一行数据,或者在读取 `limit` 个字节后停止。

13. `seek(offset, whence=io.SEEK_SET)`: 移动文件指针到给定的 `offset` 位置。`whence` 告诉 `seek()` 是从文件开始计算偏移(`io.SEEK_SET`)、从当前位置计算(`io.SEEK_CUR`),或从文件末尾计算(`io.SEEK_END`)。

14. `seekable()`: 如果流支持随机访问,则返回 True。

15. `tell()`: 返回当前文件指针的位置。

16. `truncate(size=None)`: 将文件截断到最多 `size` 个字节。如果没有指定 `size`,则截断到当前位置。

17. `writable()`: 如果流可以写入,则返回 True。

18. `write(b)`: 将字节串 `b` 写入文件,并返回写入的字节数。

19. `__init__(raw, buffer_size=DEFAULT_BUFFER_SIZE)`: 初始化 BufferedRandom 实例。`raw` 是一个必须提供的参数,是一个支持 `read`和 `write` 操作并且可查找的底层流对象。

现在我们已经了解了如何使用这个类,下面是一个使用 `BufferedRandom` 类的简单示例:


import io

# 打开文件进行二进制读写操作
with open("example.bin", "wb+") as file:
    # 包装文件进 BufferedRandom 
    buffered_random = io.BufferedRandom(file)

    # 写入一些数据
    buffered_random.write(b"Hello World")

    # 移动文件指针到文件开头
    buffered_random.seek(0)

    # 读取数据
    print(buffered_random.read())  # 输出: b'Hello World'

    # 刷新缓冲区,确保所有数据都写入了文件
    buffered_random.flush()

    # 通过 tell() 获取当前文件指针的位置
    position = buffered_random.tell()
    print(position)  # 输出: 11(如果 "Hello World" 已写入)

    # 关闭流
    buffered_random.close()


该示例展示了如何打开一个文件,对其进行读写操作,并使用`BufferedRandom`的方法进行基本的文件流操作。记住,使用完流后总是应该关闭它以释放系统资源。

BufferedReader

`BufferedReader` 类是 Python 中 `io` 模块提供的一个基于缓冲区的读取接口,用于高效地处理文件和流的读取操作。它包装一个原始的可读 IO 对象,并添加一个缓冲层来优化读取操作和减少实际的系统调用次数。下面是 `BufferedReader` 类的一些关键方法和属性的说明和用法演示。

1. `close()`
   使用这个方法来关闭流。一旦流被关闭,任何进一步的操作都会引发 `ValueError`。

2. `detach()`
   分离底层的原始流(通常是一个 File 对象)并返回它。之后,缓冲区将不再可用。

3. `fileno()`
   返回被包装的原始流的文件描述符。

4. `flush()`
   刷新缓冲区的内容,确保所有暂存的数据被写入到底层的原始流中。

5. `isatty()`
   检查底层的原始流是否连接到一个终端。

6. `peek(size=-1)`
   返回缓冲区中的数据,但不会从缓冲区中移除这些数据。参数 `size` 可以指定返回的最多字节数量。

7. `read(size=-1)`
   读取并返回最多 `size` 个字节的数据。如果 `size` 参数未指定或为负数,则读取并返回所有剩余的数据。

8. `read1(size=-1)`
   类似于 `read()`,但它在读取数据时最多只进行一次底层的原始流读取,从而可能返回少于 `size` 指定的字节数。

9. `readable()`
   如果流是可读的,则返回 `True`。

10. `readinto(b)`
    将数据读取到提供的可变字节数组 `b` 中,并返回读取的字节数。

11. `readline(size=-1)`
    读取一行,可以使用 `size` 参数限制最多读取的字节数。

12. `seek(offset, whence=io.SEEK_SET)`
    改变流中的位置。`offset` 表示相对于 `whence` 指定的位置移动多少个字节。

13. `seekable()`
    如果流支持随机访问,则返回 `True`。

14. `tell()`
    返回当前流中的位置。

15. `truncate(size=None)`
    将流的大小截为 size 字节。如果 size 是当前文件位置或未指定,当前位置将成为文件的新结尾处。

以下是如何使用 `BufferedReader` 的简单示例:


# 打开文件,返回 BufferedReader 对象
with open('example.txt', 'rb') as raw_stream:
    buf_reader = io.BufferedReader(raw_stream)

    # 读取一些数据
    data = buf_reader.read(1024)

    # 读取一行数据
    line = buf_reader.readline()

    # 获得当前文件位置
    position = buf_reader.tell()

    # 检查是否为可读流
    is_readable = buf_reader.readable()
   
    # 关闭流
    buf_reader.close()

# 注意:通常不需要显式地创建 BufferedReader 对象,
# 因为 open 函数已经为你在打开文件时自动创建了它。

需要注意的是,当使用 `with` 语句时,退出代码块后会自动关闭 `BufferedReader`(因此也会关闭底层的原始流)。在大多数情况下,直接用 `open` 函数就能满足需求,而无需直接与 `BufferedReader` 打交道,因为 `open` 函数在读取模式下已经提供了缓冲读取功能。

BufferedWriter

`BufferedWriter`类是Python标准库`io`模块中提供的一个用于处理缓冲写操作的类。这个类包装了一个可写的原始流(比如文件对象),使得对流的写入操作变得更高效,因为它以块的形式而不是每次一个字节进行写入。让我们通过一些主要方法来逐步了解如何使用`BufferedWriter`。### 构造方法


from io import BufferedWriter, DEFAULT_BUFFER_SIZE, RawIOBase

raw_stream = open('somefile.bin', 'wb')
buffered_writer = BufferedWriter(raw_stream, buffer_size=DEFAULT_BUFFER_SIZE)


在这里,`raw_stream`是一个原始的可写流,`DEFAULT_BUFFER_SIZE`是缓冲区大小的默认值。

### 常用方法和属性
- `write(b)`:向缓冲区写入一个字节字符串`b`,如果缓冲区满了,它将自动写入到底层的原始流中。
- `flush()`:将缓冲区中的所有数据强制写入到原始流中,不论缓冲区是否已满。
- `close()`:首先调用`flush()`将剩余数据写入原始流,然后关闭流。
- `detach()`:将缓冲区与其下层的原始流分离,并返回这个原始流。分离之后,缓冲区不可再用。
- `fileno()`:返回底层原始流的文件描述符。
- `isatty()`:检查底层原始流是否连接到一个终端。
- `seek(offset, whence=SEEK_SET)`:移动文件指针到指定位置。
- `tell()`:返回当前文件指针的位置。
- `truncate(size=None)`:调整文件大小至size字节,如果size是None,则调整至当前文件指针位置。
- `writable()`:返回缓冲区是否支持写操作。

### 属性
- `closed`:返回流是否已经被关闭。
- `raw`:返回`BufferedWriter`实例内部的原始流对象。

### 示例 - 写入数据到文件


with open('example.bin', 'wb') as raw_stream:
    writer = BufferedWriter(raw_stream)
    writer.write(b'Hello, World!')
    writer.flush()  # 确保写入到底层的原始流


在这个例子中,我们打开了一个文件,然后创建了一个`BufferedWriter`来写入数据。最后,我们调用`flush()`来确保所有数据都已经被写入到底层的文件中。

总结起来,`BufferedWriter`提供了一种有效的写操作方式,通过缓冲机制减少对原始流的写入次数,这对于大量的数据写入是非常有益的。要通过底层原始流写入数据,可以创建一个`BufferedWriter`对象,然后使用该对象的`write`方法来写入数据。如果需要确保所有数据都及时写入文件,可以使用`flush`方法。当不再需要写入数据时,应该使用`close`方法来关闭流。

FileIO

`FileIO`类是Python的`io`模块中低级的文件操作类,用于处理文件的基本输入输出操作。它直接与操作系统底层的文件I/O接口进行交互,而没有像其它如`TextIOWrapper`那样的文本编解码层。以下是关于`FileIO`类中方法的详细介绍和演示。

首先,我写一个Python脚本来展示`FileIO`类的用法:


import os
from io import FileIO, SEEK_SET, SEEK_CUR, SEEK_END

# 打开文件
file_path = 'example.txt'
f = FileIO(file_path, 'w+')

# 写入数据
f.write(b"Hello, World!")

# 查找文件位置
print(f.tell())  # 输出当前位置,应为12,因为"Hello, World!"共有12个字节。

# 从文件开始处偏移6个字节
f.seek(6, SEEK_SET)
print(f.read(5))  # 读取5个字节,输出"World"

# 从当前位置向前偏移7个字节到文件开头
f.seek(7, SEEK_CUR)
print(f.tell())  # 输出当前位置。

# 检查文件是否可读
print(f.readable())  # 输出True,因为我们以'w+'模式打开文件,可以进行读操作。

# 检查文件是否可写
print(f.writable())  # 输出True,因为文件是以写模式打开的。

# 检查是否是TTY设备(终端)
print(f.isatty())  # 通常返回False,除非文件绑定到一个终端设备。

# 检查文件是否支持随机访问
print(f.seekable())  # 输出True,因为普通文件支持seek操作。

# 截断文件到当前位置
f.truncate()

# 获取文件描述符
print(f.fileno())  # 输出一个整数,代表文件描述符。

# 关闭文件
f.close()

# 检测文件是否已关闭
print(f.closed)  # 输出True,表示文件已关闭。

现在我们逐个地解释上面脚本中的方法:

1. `__init__`: 创建FileIO对象时调用。

2. `close`: 关闭文件。关闭后文件将不可进一步读写。

3. `fileno`: 返回文件描述符的整数值。

4. `isatty`: 返回文件是否连接到TTY设备的布尔值。

5. `read`: 读取至多`size`字节的数据,返回为字节串。

6. `readable`: 返回文件是否以可读模式打开的布尔值。

7. `readall`: 读取文件中所有数据,返回为字节串。

8. `readinto`: 将数据读入一个提供的可变字节串中。

9. `seek`: 移动文件读写位置到指定位置。

10. `seekable`: 返回文件是否支持随机访问的布尔值。

11. `tell`: 返回当前文件位置。

12. `truncate`: 将文件截断至最多`size`字节。

13. `writable`: 返回文件是否以可写模式打开的布尔值。

14. `write`: 将字节串写入文件。

注意,`readinto`、`seek`、`truncate`和`write`等方法在具体使用时,可能需要更详细的操作说明和参数传递。

这些方法使得`FileIO`适用于需要低级别文件操作和对性能要求较高的场景。它们为Python提供了直接访问底层文件I/O接口的能力,但也因此需要开发者具备一些关于文件系统和文件描述符的基础知识。

BytesIO

在下面的Python代码文件中,我将介绍并演示`BytesIO`类中的所有方法。`io.BytesIO`是Python中的一个内置类,用来在内存中读写字节数据,类似于文件对象。

from io import BytesIO

# 创建一个BytesIO对象
with BytesIO() as stream:

 # 写入数据
 stream.write(b'This is a demo.')
 # 获取当前位置
 print(f"Current position: {stream.tell()}")

 # 示范seek方法
 stream.seek(0)  # 移动到文件开头
 print(f"New position after seek(0): {stream.tell()}")

 # 读取数据
 print(f"Read data: {stream.read()}")

 # 示范readable, writable, seekable
 print(f"Readable: {stream.readable()}")
 print(f"Writable: {stream.writable()}")
 print(f"Seekable: {stream.seekable()}")

 # 以下是演示BytesIO类中所有方法的代码
 # 因为是解释性介绍,实际运行时请分别取消每一段的注释来查看效果

 #读写操作合一
 write_data = b'Combine read and write'
 stream.write(write_data)
 stream.seek(0)
 print(stream.read())

 #读取指定大小的数据
 data = stream.read(5)  # 读取前5个bytes
 print(data)

 #读取一行
 line = stream.readline()
 print(line)

 #读取所有行
 lines = stream.readlines()
 for line in lines:
    print(line)

 #获取当前位置
 print(stream.tell())

 #更改流位置
 stream.seek(0)  # 移动到开头
 stream.seek(5)  # 移动到第五个byte
 stream.seek(-1, 2)  # 移动到倒数第一个byte

 #获取全部数据
 all_data = stream.getvalue()
 print(all_data)

 #创建可读写的内存视图
 buffer = stream.getbuffer()
 print(buffer)

 #检查是否为TTY设备
 print(stream.isatty())

 #告知对象的大小
 print(stream.__sizeof__())

 #构造函数和析构函数
 def __init__(self):
     """ Initialize the stream. """
     super().__init__()

 def __del__(self):
     """ Destructor to clean up the stream. """
     self.close()

 del buffer#因为流已经被传入低级语言,大小不能改,而限制流大小和关闭流会通过视图尝试改变流底层大小而报错
 # 清空和关闭流
 stream.truncate(0)
 stream.close()

 # 验证流关闭
 print(f"Closed: {stream.closed}")

请注意,您需要根据自己的需求取消一些示例代码的注释来运行它们。该代码仅供示范如何使用`BytesIO`类及其方法;它不代表真实的Python库中的实现。在实践中,`BytesIO`实例在调用`close()`后,大多数方法将无法再使用,并且尝试这么做通常会抛出`ValueError`异常,因为流已经关闭。

StringIO

虽然我无法直接创建和运行代码文件,我可以提供一个`StringIO`类的使用详细介绍,并演示如何在Python代码中使用这个类的所有方法。这个类是`io`模块中用于处理字符串对象的类,类似于在内存中处理文件对象。

下面是这个类每个方法的说明和演示:

from io import StringIO

# 初始化StringIO对象,可以选择传入一个字符串作为初始值
initial_value = 'Hello, world!\nThis is an example.'
sio = StringIO(initial_value)

# 使用getvalue获取全部内容
contents = sio.getvalue()
print('Initial content:\n', contents)

# 使用read方法读取内容,可指定size读取特定长度的字符串
# 如果size为负或不提供,read所有内容直到EOF
sio.seek(0)
print('Read content:\n', sio.read())

# 使用readable方法检查对象是否可读
print('Is readable:', sio.readable())

# 使用readline方法逐行读取内容
sio.seek(0)
print('First line:', sio.readline())
print('Second line:', sio.readline())

# 使用seek方法定位到流中的新位置
# whence 参数是可选的,0为相对于流的起始位置,1为相对当前位置,2为相对于流的末尾
sio.seek(0, 0) # 移动到文件开头

# 使用seekable方法检查流是否支持random access
print('Is seekable:', sio.seekable())

# 使用tell方法获取当前文件位置
print('Current position:', sio.tell())

# 使用truncate方法截断到指定位置的流
# pos参数为截取位置,默认为当前位置,流的位置不变
sio.truncate(10)
print('Truncated content:\n', sio.getvalue())

# 使用writable方法检查对象是否可写
print('Is writable:', sio.writable())

# 使用write方法写入字符串
# 返回写入的字符数,通常等于字符串的长度
sio.seek(0)
sio.write('Added content ')
print('Content after write:\n', sio.getvalue())

# 判断对象是否已经关闭
print('Is closed before close:', sio.closed)

# 使用close方法关闭StringIO对象
# 之后尝试任何操作将会抛出ValueError
sio.close()
print('Is closed after close:', sio.closed)

# 尝试再次写入将会抛出ValueError
try:
 sio.write('This will fail')
except ValueError as e:
 print('Error:', e)

# 字符串IO对象没有缓冲区,所以 line_buffering 和 newlines 属性没有实际意义
# 但是它们仍然是StringIO类的成员属性
print('Line buffering:', sio.line_buffering)
print('Newlines:', sio.newlines)

每个方法在使用的时候需要确保`StringIO`对象是开启的,一旦调用了`close()`方法,对象就会关闭,接下来对它进行的任何操作都会抛出`ValueError`。
 

基本IO代码示例

import io
import struct
#示例代码包括:
#- 打开文件
#- 读取文件内容
#- 写入内容到文件
#- 移动文件指针
#- 检查文件是否可操作
#- 使用with语句自动管理文件资源
#- 读写二进制文件
#- 使用try-except语句处理潜在错误
# 文件操作演示
def main():
    # 写入文本到文件
    file_path_text = "D:\\a.txt"
    try:
        # 使用with语句打开文件,保证异常发生时自动关闭文件
        with open(file_path_text, 'w', encoding='utf-8') as file:
            file.write("Hello, World!\n")
            file.write("这是另一行文本。\n")

        # 追加内容到文件
        with open(file_path_text, 'a', encoding='utf-8') as file:
            file.writelines(["追加第一行。\n", "追加第二行。\n"])

        # 读取文件内容
        with open(file_path_text, 'r', encoding='utf-8') as file:
            print("读取单行:", file.readline(), end='')  # 读取一行
            print("读取所有行为列表:", file.readlines())  # 读取所有行为列表

        # 在文件中移动指针
        with open("D:\\a.txt", "r+", encoding="utf-8") as file:
         file.seek(0, 0)  # 移动文件指针到文件开头。 0 表示文件的开头, 0 代表偏移字节的数量
         content = file.read()  # 从当前位置读取全部内容。 当 size 为负或未给定时,读取并返回文件中剩余的所有字节
         print(f"文件内容:\n{content}")

         file.write("在文件开头添加的内容。\n")  # 向文件写入内容。s 字符串是要写入打开文件的内容,此函数将返回实际写入的字符数
         file.flush()  # 将内存缓冲区的内容写入硬盘。用于强制将文件写入磁盘

         lines = file.readlines()  # 读取所有行。当 hint 为负或未给定时返回列表
         print(f"读取到的所有行:\n{lines}")

         file.writelines(["一行新的内容。\n", "另一行新的内容。\n"])  # 向文件写入多行内容。lines 表示要写入的行列表
         file.flush()

         print(f"当前文件位置:{file.tell()}")  # 打印当前文件的位置。当前文件位置的返回值以文件的开头为起始点(以字节为单位)。

         file.close()  # 关闭文件

        # 读写二进制文件
        file_path_binary = "D:\\PRIME.DAT"
        with open(file_path_binary, 'wb') as file:
            file.write(b'\x00\x01\x02\x03\x04')  # 写入一些二进制数据

        with open(file_path_binary, 'rb') as file:
            binary_content = file.read()
            print("二进制文件内容:", list(binary_content))

    except IOError as e:
        print("IO操作中出现错误:", e)

    except Exception as e:
        print("未知错误:", e)
    # 打开文件a.txt以进行读取,不限定编码会出现下面的错误
    #UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 35: illegal multibyte sequence
    with open('D:\\a.txt', 'r', encoding='utf-8') as file:
        # 读取所有内容并打印
        content = file.read()
        print('a.txt content:', content)

        # 回到文件的开始
        file.seek(0)

        # 读取第一行
        first_line = file.readline()
        print('First line:', first_line)

        # 读取所有行
        all_lines = file.readlines()
        print('All lines:', all_lines)

        # 获取当前指针的位置
        current_position = file.tell()
        print('Current position:', current_position)

    # 打开文件PRIME.DAT以进行写入
# 使用io.BufferedWriter增加了一个写入缓冲区
    with open('D:\\PRIME.DAT', 'wb') as raw_file:
        file = io.BufferedWriter(raw_file)

        # 写入字符串数据,被编码为二进制格式,这样写入,用记事本打开文件就能看到内容
        file.write('Prime Numbers are important!\n'.encode('utf-8'))

        # 写入整数列表
        primes = [2, 3, 5, 7]
        for prime in primes:
            # 将整数转换为二进制数据
            file.write(struct.pack('i', prime))

        # 刷新缓冲区,确保所有数据都已经写入文件
        file.flush()

    # 打开文件PRIME.DAT以进行读取
    # 使用io.BufferedReader增加了一个读取缓冲区
    with open('D:\\PRIME.DAT', 'rb+') as raw_file:
        file = io.BufferedReader(raw_file)#专用于读的缓冲流
        file1 = io.BufferedWriter(raw_file)#专用于写的缓冲流
        # readline示例
        message = file.readline()
        print(message.decode('utf-8'), end='')

        # read和unpack整数
        prime_bytes_size = struct.calcsize('i')
        while True:
            prime_bytes = file.read(prime_bytes_size)
            if not prime_bytes:
                break
            prime = struct.unpack('i', prime_bytes)[0]
            print(prime)

        # seek和tell的示例
        print("Current file position (after reading primes):", file.tell())
        file.seek(0)  # 移动到文件开头
        print("File position after seeking to beginning:", file.tell())

        # 重新读取文件行
        all_lines = file.readlines()
        print("Re-read lines:", all_lines)

        # 在文件尾部添加额外的素数使用writelines
        # 首先需要移动到文件尾部
        file.seek(0, io.SEEK_END)
        # 将数组转换为二进制格式并使用writelines
        extra_primes = [11, 13]
        bytes_to_write = [struct.pack('i', p) for p in extra_primes]
        file1.writelines(bytes_to_write)

        # 再次刷新文件以确保全部写入
        file.flush()
        file1.flush()
        # 这里我们不需要显式调用close,因为with语句块结束时会自动关闭文件。
if __name__ == "__main__":
    main()
"""
执行结果:
读取单行: Hello, World!
读取所有行为列表: ['这是另一行文本。\n', '追加第一行。\n', '追加第二行。\n']
文件内容:
Hello, World!
这是另一行文本。
追加第一行。
追加第二行。

读取到的所有行:
[]
当前文件位置:165
二进制文件内容: [0, 1, 2, 3, 4]
a.txt content: Hello, World!
这是另一行文本。
追加第一行。
追加第二行。
在文件开头添加的内容。
一行新的内容。
另一行新的内容。

First line: Hello, World!

All lines: ['这是另一行文本。\n', '追加第一行。\n', '追加第二行。\n', '在文件开头添加的内容。\n', '一行新的内容。\n', '另一行新的内容。\n']
Current position: 165
Prime Numbers are important!
2
3
5
7
Current file position (after reading primes): 45
File position after seeking to beginning: 0
Re-read lines: [b'Prime Numbers are important!\n', b'\x02\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x07\x00\x00\x00']
"""
逐行遍历文件:

f = open("demofile.txt", "r")
for x in f:
  print(x)

8、容器 

迭代器 VS 可迭代对象(Iterable)

字符串、列表、元组、字典和集合都是可迭代的对象。它们是可迭代的容器,您可以从中获取迭代器(Iterator)。

所有这些对象都有用于获取迭代器的 iter() 方法

要把对象/类创建为迭代器,必须为对象实现 __iter__() 和 __next__() 方法

为了防止迭代永远进行,我们可以使用 StopIteration 语句。

在 __next__() 方法中,如果迭代完成指定的次数,我们可以添加一个终止条件来引发错误

mytuple = ("apple", "banana", "cherry")
myit = iter(mytuple)
print(next(myit))
print(next(myit))
print(next(myit))
for x in mytuple:#我们也可以使用 for 循环遍历可迭代对象
 print(x)
mystr = "banana"
myit = iter(mystr)
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
class MyNumbers:
 def __iter__(self):
  self.a = 1
  return self

 def __next__(self):
  if self.a <= 20:
   x = self.a
   self.a += 1
   return x
  else:
   raise StopIteration

myclass = MyNumbers()
myiter = iter(myclass)

for x in myiter:
 print(x)

以下是每种容器类型的特性介绍:

列表 (List): Python 中的列表是有序的可变容器,允许存储重复的元素。列表非常灵活,支持添加、删除和更改元素。它通过方括号`[]`定义,并允许包含不同类型的数据。

列表的 remove() 方法仅删除首次出现的指定值。

元组 (Tuple): 元组是不可变的容器,一旦创建,元组中的数据就不能修改。元组用圆括号`()`来创建,它们通常用于保证数据的完整性,确保数据不被更改。

集合 (Set): 集合是一个无序的容器,用于存储唯一的元素,也就是说它不允许有重复的成员。集合用花括号`{}`来定义,非常适合进行集合操作,如并集、交集、差集等。

字典 (Dictionary): 字典存储键值对,是一种可变容器,通过键来唯一标识每个元素值。字典用花括号`{}`来定义,其中的键必须是唯一的。它提供了非常快的查找能力,因为它使用哈希算法来存储数据。

命名元组 (Namedtuple): 命名元组是元组的扩展,它允许每个位置元素都有一个名称,即字段名,可以通过名称来访问元组中的数据,而不是仅通过索引位置。

双端队列 (Deque): 双端队列是一种优化的列表,可以快速从两端添加或弃元素的容器。它在`collections`模块下,适合用于需要首尾操作的场合。

计数器 (Counter): 计数器是一个字典的子类,用于跟踪值的出现次数。它也是在`collections`模块之下,能够方便地为每个元素计数。

有序字典 (OrderedDict): 有序字典是字典的子类,它记住了元素添加的顺序,也就是说,当遍历OrderedDict时,项会按照它们插入的顺序返回。

默认字典 (Defaultdict): 默认字典是另一种字典的变种,它为字典提供默认值。当直接访问一个尚未加入到字典中的键时,defaultdict会自动为该键生成一个默认值。

这些容器类型是Python编程中使用非常频繁的数据结构,各有各的应用场景。

#列表###############################
def lists():
 #创建列表:
 a=[]
 example_list = [1, 2, 3, 4, 5]  # 创建一个包含数字的列表
 #访问列表元素(通过索引):
 print(example_list[0])  # 输出第一个元素,结果为1
 #遍历列表
 for i in range(len(example_list)):
    #print(f'Index: {i}, Value: {example_list[i]}')
  print(example_list[i],end=' ')
 print("for i")
 # 修改列表元素:
 example_list[0] = 'a'  # 更改第一个元素的值
 # 添加元素:
 # 使用 append() 在列表末尾添加元素:
 example_list.append(6)  # 在列表末尾添加新元素6
 # 使用 insert() 在指定位置插入元素:
 example_list.insert(1, 'b')  # 在索引1的位置插入'b'
 # 删除元素:
 # 使用 pop() 删除并返回指定位置的元素(默认为最后一个):
 last_item = example_list.pop()  # 删除并返回最后一个元素
 # 使用 remove() 删除第一个匹配的元素:
 example_list.remove('a')  # 删除列表中的'a'
 # 使用 del 语句删除指定位置的元素:
 del example_list[1]  # 删除索引为1的元素
 # 列表切片(用于访问部分元素):
 sublist = example_list[1:3]  # 获取索引从1到2的元素(不包括3)
 # 列表合并:使用 + 运算符合并列表:
 new_list = example_list + [7, 8, 9]
 #使用 extend() 方法合并列表:
 example_list.extend([7, 8, 9])  # 将另一个列表的元素添加到当前列表
 print(example_list,"aa",sep=" ")
 # 列表排序:
 # 使用 sort() 对列表进行排序:
 numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
 numbers.sort()  # 将列表升序排序
 print("升序",numbers)  # 输出排序后的列表
 numbers.sort(reverse=True)  # 将列表降序排序
 print("降序",numbers)  # 输出排序后的列表
 def equal(a):
     return a%2
 numbers.sort(reverse=True,key=equal)  # 将列表自定义排序,注意写equal(a)不行
 print("自定义",numbers)  # 输出排序后的列表
 # 列表反转:
 # 使用 reverse() 对列表进行原地反转:['b', 3, 4, 5, 7, 8, 9]到[9, 8, 7, 5, 4, 3, 'b']
 example_list.reverse()  # 反转整个列表
 # 列表长度:
 # 使用 len() 函数获取列表长度:
 length = len(example_list)  # 获取列表元素个数
 # 查找元素:
 # 使用 index() 查找元素的索引:
 print(example_list)
 try:
  position = example_list.index(3)  # 返回元素3的索引,如果不存在则报错
  position=example_list.index(7,1,2)#在[1,2)找7(不包括2),找不到报错
 except Exception as e:
  print(e)
 #如果想倒着找,可以reverse()反转后index,或者使用enumerate()和reversed()迭代列表
 #使用 reversed(my_list) 来获取 example_list 的逆序迭代器,以便从列表的末尾开始查找。
 # 再使用 enumerate() 函数去遍历这个逆序迭代器,并返回每个元素的索引(这里的索引是基于逆序迭代器的位置,即从末尾开始)和元素值。
 for index,element in enumerate(reversed(example_list)):
     if element==3:#index是索引,element是元素
         print("find 3 ",len(example_list)-1-index)#换算成原列表的索引
         break
     else :
         print("not found",index,element,sep=" ")
# 使用 in 检查元素是否在列表中:
 print(3 in example_list ) # 检查3是否在列表中,返回True或False
 ###列表推导通过一个表达式创建一个新的列表。语法为:
 #[expression for item in iterable if condition]
 #这里,expression 是当前的列表项,item 是来自可迭代对象 iterable 的成员,condition 是一个布尔表达式,用于过滤可迭代对象的成员。
 #1. 提取列表中的偶数:
 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
 evens = [num for num in numbers if num % 2 == 0]
 # 计算每个元素平方 列表推导式(用于创建新列表或对列表进行过滤和变换):
 squares = [x**2 for x in range(10)]  # 创建一个包含前十个整数平方的列表
 #squals [0, 1, 4, 9, 16, 25, 36, 49, 64, 81],**是指数运算符
 print("squals",squares)
 #3. 转换列表中字符串的大小写:
 words = ["hello", "world"]
 upper_words = [word.upper() for word in words]
 #4. 使用多个迭代器:
 rows = range(1, 4)
 cols = range(1, 3)
 cells = [(row, col) for row in rows for col in cols]
 print("cells ",cells)#cells  [(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2)]
lists()
#元组##########################################################
def tuples():
 ### 创建元组
 #创建元组最简单的方法是使用圆括号`()`,将元素用逗号分隔。
 t = (1, 2, 3)  # 创建一个包含1, 2, 3的元组
 empty_tuple = ()  # 创建一个空元组
 single_element_tuple = (4,)  # 创建一个单元素元组,注意后面的逗号
 # 原理:在Python中,元组是通过圆括号中的逗号进行元素分隔来定义的。单个元素后面的逗号是区分单个对象还是元组的必要语法。
 ### 访问元组元素
 # 使用索引可以访问元组中的元素,索引从0开始。
 t = (1, 2, 3)
 print(t[0])  # 输出1,访问第一个元素
 print(t[1])  # 输出2,访问第二个元素
 print("single ",single_element_tuple[0])
 #原理:元组是一个有序容器,每个元素在内存中有固定的位置,使用索引可以直接访问对应位置的元素。
 ### 分片操作
 # 分片(Slicing)是获取元组子集的方法,它通过冒号(`:`)来指定开始和结束的索引。
 t = (1, 2, 3, 4, 5)
 sub_t = t[1:3]  # 结果是(2, 3)
 print(sub_t)
 #原理:分片操作创建了原元组的一个浅拷贝,即一个新元组,其中包含原元组指定范围内的元素。在内存中,这是一个新的对象。
 ### 修改元组 (不可能直接做到)
 # 因为元组是不可变的,所以不能直接修改一个已创建的元组元素的值,但可以通过连接或其他方法来生成一个包含修改值的新元组。
 t = (1, 2, 3)
 # t[0] = 4  # 这样做会抛出TypeError异常
 print(t)
 t = (4,) + t[1:]  # 创建一个新元组(4, 2, 3)
 print("after change",t)
 #原理:由于元组是不可变的,因此不能就地修改。但通过组合和分片,可以创建内容改变的新元组。
 ### 拼接和重复
 #元组可以使用`+`运算符进行拼接,使用`*`运算符进行重复。
 t1 = (1, 2, 3)
 t2 = (4, 5, 6)
 t3 = t1 + t2  # 结果是(1, 2, 3, 4, 5, 6)
 repeated_t = t1 * 2  # 结果是(1, 2, 3, 1, 2, 3)
 #原理:拼接和重复实际上在内存中创建了一个新的元组,是元组不可变性的一种体现。
 ### 使用`in`检查元素,你可以检查某个元素是否存在于元组中。
 t = (1, 2, 3)
 print(2 in t)  # 输出True
 # 原理:`in`关键字用于检查序列中是否存在特定元素。
 ### 元组的其他操作和函数
 # 元组支持一些内置函数,例如`len()`、`max()`、`min()`、`count()`和`index()`。
 t = (1, 2, 3, 2)
 len_t = len(t)  # 结果是4
 max_t = max(t)  # 结果是3
 min_t = min(t)  # 结果是1
 count_t = t.count(2)  # 结果是2,因为元素2出现了两次
 index_t = t.index(3)  # 结果是2,因为元素3的索引是2
 # 原理:这些函数都是以读取和计算元组内容为基础,不改变原元组的结构。
 # 由于元组是不可变的,不存在增加、删除或更改元素的方法。这种不可变性提供了一定程度上的安全性,因为你可以确信一旦创建了元组,它就不能被意外地改变。
tuples()
#集合####################################################
def sets():
 #集合(Set)是Python中的一个基本数据结构类型,类似数学上的集合概念,它具有以下特点:
 # 1. **无序性**:集合中的元素是无序的,不能通过索引来访问。
 # 2. **唯一性**:集合中的元素是唯一的,不允许有重复的值。
 # 3. **可变性**:集合是可变的,可以动态地添加或删除元素。
 # 下面是Python中集合常见的操作以及它们的原理:
 ### 创建集合
 # 使用大括号
 my_set = {1, 2, 3}
 # 使用 set() 函数
 a=[1,2,3]
 my_set = set(a)
 ### 添加元素
 my_set.add(4)  # 添加元素 4 到集合中
 # 原理:`.add()` 函数将元素添加到集合中,如果元素已存在,则操作无任何影响。
 ### 更新集合
 my_set.update([5, 6])  # 更新集合,添加多个元素
 # 原理:`.update()` 方法接收一个列表、集合或任何可迭代对象,将其元素添加到当前集合中,忽略已存在的元素。
 ### 删除元素
 my_set.discard(4)  # 移除元素 4,如果元素不存在也不会报错
 my_set.remove(5)  # 移除元素 5,如果元素不存在会报错
 my_set.pop()  # 随机移除一个元素并返回该元素,集合不能为空
 # 原理:
 # - `.discard()` 方法用于移除指定的集合元素,不存在的元素不会引发错误。
 # - `.remove()` 方法在移除不存在的元素时会抛出`KeyError`。
 # - `.pop()` 方法从集合中随机移除一个元素并返回它,如果集合为空则抛出`KeyError`异常。
 ### 清空集合
 my_set.clear()  # 移除集合中的所有元素
 # 原理:`.clear()` 方法将集合内容清空。
 ### 检查元素是否存在
 my_set.update([1,2,3])
 if 3 in my_set:
     print("Element 3 is in the set")
 #原理:`in` 关键字用于检查元素是否存在于集合中。
 ## 集合的数学操作
 set_a = {1, 2, 3,4}
 set_b = {3, 4, 5,6}
 # 交集,&  {3, 4}
 intersection = set_a & set_b
 print("& ",intersection)
 # 或 set_a.intersection(set_b)
 # 并集,|  {1, 2, 3, 4, 5, 6}
 union = set_a | set_b
 print("| ",union)
 # 或 set_a.union(set_b)
 # 差集,-  {1, 2}
 difference = set_a - set_b
 print("- ",difference)
 # 或 set_a.difference(set_b)
 # 对称差集(环合,存在于一个集合中但不同时存在于两个集合中的元素),^  {1, 2, 5, 6}
 symmetric_difference = set_a ^ set_b
 print("^ ",symmetric_difference)
 # 或 set_a.symmetric_difference(set_b)
 # 原理:
 # - 交集`: &` 返回两个集合中都存在的元素。
 # - 并集`: |` 返回存在于至少一个集合中的所有元素。
 # - 差集`: -` 返回存在于第一个集合但不在第二个集合中的元素。
 # - 对称差集`: ^` 返回只存在于一个集合中的元素。
 ### 其他函数和操作
 #- `len(my_set)`:返回集合中元素的数量。
 print("len",len(my_set))
 #- `set_a.issubset(set_b)`:如果`set_a`中的所有元素`set_b`中也存在,则`set_a`是`set_b`的子集。
 print("& in |",intersection.issubset(union))
 #- `set_a.issuperset(set_b)`:如果`set_b`中的所有元素`set_a`中也存在,则`set_a`是`set_b`的超集。
 print("| have &",union.issuperset(intersection))
 #- `set_a.isdisjoint(set_b)`:如果`set_a`和`set_b`没有相同的元素,则这两个集合是不相交的
 print("- have not b",difference.isdisjoint(set_b))
sets()
#字典################################################
def Dictionarys():
 #在Python中,字典是一种可变的数据结构,用于存储键值对的集合。字典的键必须是唯一的并且是不可变类型,比如字符串、数字或元组。
 # 字典中的值可以是任何数据类型。以下是一些在Python字典上进行的常见操作以及它们的原理。
 ### 创建字典
 # **示例代码**:
 # 创建空字典
 my_dict = {}
 # 使用键值对创建字典
 my_dict = {'apple': 1, 'banana': 2, 'cherry': 3}
 #**原理**:字典可以通过花括号 `{}` 创建空字典,或者包含键值对来初始化字典,键和值之间用冒号 `:` 分隔。
 #使用fromkeys() 创建新字典:
 #可以使用fromkeys() 方法从给定的键创建一个新的字典,每个键对应的值都是相同的。
 keys = {'a', 'b', 'c'}
 value = 1
 new_dict = dict.fromkeys(keys, value)
 # new_dict 现在是 {'a': 1, 'b': 1, 'c': 1}
 ### 访问元素
 # **示例代码**:
 # 访问字典中的值
 apple_quantity = my_dict['apple']
 #**原理**:访问字典中的元素可以通过键作为索引来实现。
 ### 修改和添加键值对
 #**示例代码**:
 # 更新键值对
 my_dict['banana'] = 5
 # 添加新键值对
 my_dict['durian'] = 10
 #**原理**:可以通过赋值操作直接修改字典中的值,或者添加一个新的键值对。
 #使用update() 合并字典:
 #可以用一个字典来更新另一个字典,其中的键值对会被添加到原字典中,如果有相同的键,则会更新其值。
 dict1 = {'a': 1, 'b': 2}
 dict2 = {'b': 3, 'c': 4}
 dict1.update(dict2)
 # dict1 现在是 {'a': 1, 'b': 3, 'c': 4}
 ### 删除键值对
 #**示例代码**:
 # 删除键是 'banana' 的条目
 del my_dict['banana']
 # 删除并返回键是 'cherry' 的条目
 cherry_quantity = my_dict.pop('cherry')
 #popitem() 通常用于获取并删除最后一个插入的项
 print(my_dict)
 k,y=my_dict.popitem()
 print("after popitem()",my_dict,"poped key ",k," poped value ",y)
 #**原理**:`del` 语句用于删除指定键的键值对;`pop` 方法删除指定键的键值对并返回该键对应的值。
 ### 获取所有键或值
 #**示例代码**:
 # 获取所有键
 keys = my_dict.keys()
 k=set(keys)
 print("k",k)
 for k in keys:
     print(k)
 # 获取所有值
 values = my_dict.values()
 print("keys ",keys," values ",values)
 #**原理**:`keys` 方法返回一个包含字典所有键的视图;`values` 方法返回一个包含字典所有值的视图。
 # 视图会动态反映字典内容的任何变化。如果你在创建了键视图之后修改了字典(比如添加或删除键),键视图会显示这些变化。
 #视图可以通过循环访问或转为列表访问
 ### 检查键存在性
 #**示例代码**:
 # 检查 'apple' 是否为字典的键
 is_apple_key = 'apple' in my_dict
 #**原理**:使用 `in` 关键字可以检查一个键是否存在于字典中。
 ### 字典遍历
 #**示例代码**:
 # 遍历所有键值对
 for key, value in my_dict.items():
     print(f"{key}: {value}")
 #**原理**:`items` 方法返回一个包含所有键值对的视图,可以使用for循环对其进行遍历。
 ### 字典的大小
 #**示例代码**:
 # 获取字典中键值对的数量
 size = len(my_dict)
 #**原理**:使用内置函数 `len` 可以获取字典的大小,即其中键值对的数量。
 ### 清空字典
 #**示例代码**:
 # 清空字典中的所有条目
 my_dict.clear()
 #**原理**:`clear` 方法移除字典中的所有项,结果是一个空的字典结构。
 #此外,还有很多其他高级的字典操作和方法,例如使用 `get` 获取键对应的值,如果键不存在则返回一个默认值None
 print("get(apple) ",my_dict.get('apple','__default=0'))#第二个参数指定默认值__default=0
 # 使用 `setdefault` 设置某个键的默认值(如果该键不存在于字典中),等等。
 my_dict.setdefault('apple','a')#尝试添加apple,若此键存在则不改值,不存在则添加此键,值为a,默认None
 my_dict.setdefault('apple')
 print(my_dict)
 #字典推导(Dict Comprehension):
 #字典推导通过对一个可迭代对象里的每一个元素应用一个表达式来构建字典。语法为:
 #{key_expression: value_expression for item in iterable if condition}
 #这里的 key_expression 是字典的键,value_expression 是字典的值。
 # 示例:将一个字符串中字符的出现频率映射到一个字典
 s = "hello"
 char_freq = {char: s.count(char) for char in s}
 print("Dict Comprehension ",char_freq)
 #字典推导的用例:
 #1. 将两个列表转换成字典:
 keys = ["name", "age", "gender"]
 values = ["Alice", 25, "Female"]
 dictionary = {keys[i]: values[i] for i in range(len(keys))}
 #2. 交换字典的键和值:
 original_dict = {'a': 1, 'b': 2, 'c': 3}
 swapped_dict = {value: key for key, value in original_dict.items()}
 #3. 过滤字典项:
 original_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
 filtered_dict = {k: v for k, v in original_dict.items() if v > 2}
 #4. 使用列表项作为字典的键和值:
 list_of_tuples = [(1, 'a'), (2, 'b'), (3, 'c')]
 dict_from_list = {t[0]: t[1] for t in list_of_tuples}
 #以上便是Python字典的一些基础操作和它们背后的原理。字典由于其高效的键值对存储结构,在许多场景下是非常有用的数据类型。
Dictionarys()
##命名元组#################################
def Namedtuple():
 # 在Python中,`collections.namedtuple` 是一个工厂函数,它返回一个新定义的元组子类,这个子类是具名元素的元组,
 # 它通过名字索引元组中的数据,同时也支持普通元组的索引方式。`namedtuple` 使得代码更加可读,因为你不必再使用索引来访问元组的元素,
 # 而是可以通过更加易懂的名称来访问。
 # 使用 `namedtuple` 的基本步骤如下:
 # 1. 导入 `namedtuple` 工具。
 # 2. 定义 `namedtuple`,指定名称和字段。
 # 3. 创建 `namedtuple` 的实例。
 # 4. 访问 `namedtuple` 的字段。
 # 以下是 `namedtuple` 的操作示例:
 from collections import namedtuple
 # 定义一个 namedtuple,名为 "Person",字段有 'name', 'age', 'location'
 Person = namedtuple('Person', 'name age location')
 PPerson = namedtuple('PPerson', ['name', 'age'], defaults=[None, 25])
 # 创建一个 Person 对象
 person = Person(name="Alice", age=30, location="Wonderland")
 pperson=PPerson()
 # 访问命名元组的字段
 print(person.name)      # 输出: Alice
 print(person.age)       # 输出: 30
 print(person.location)  # 输出: Wonderland
 print(pperson.name,pperson.age)#按defaults=[None, 25],输出None 25
 # 像普通元组一样通过索引访问
 print(person[0])        # 输出: Alice
 # _fields 属性返回一个包含所有字段名称的元组
 print(Person._fields)   # 输出: ('name', 'age', 'location')
 # _asdict() 将命名元组转换为OrderedDict
 print(person._asdict()) # 输出: OrderedDict([('name', 'Alice'), ('age', 30), ('location', 'Wonderland')])
 # _replace() 创建一个新的实例,替换指定字段的值
 person_new = person._replace(name="Bob")
 print(person_new.name)  # 输出:Bob
 # _make() 用可迭代对象创建一个新的实例
 data = ['Carol', 25, 'Atlantis']
 new_person = Person._make(data)
 print(new_person)       # 输出: Person(name='Carol', age=25, location='Atlantis')
 # 除了以上常用的操作,`namedtuple` 还有一些其他特殊方法,例如:
 # - `_make(iterable)`: 从可迭代对象创建一个新的命名元组的实例。
 # - `_fields_default`: 字段默认值(Python 3.7+)。
 # 原理上,`namedtuple` 实际上是一个类,它继承自元组(tuple),并且为每个字段重载了 `__getitem__()` 方法来支持通过名称访问字段,
 # 同时也支持索引访问。这给我们提供了元组不变性的同时,增加了通过字段名访问的便利性。
Namedtuple()
#双端队列#############################
def Deque():
 #双端队列(deque,全称 double-ended queue)在 Python 中是由 `collections` 模块提供的一个类。与列表相似,
 # 双端队列支持从两端添加和移除元素,但在两端的操作上是优化过的,因此具有更高效的性能,特别是具有固定大小的队列,
 # 避免了列表在进行插入或删除操作时拥有的\( O(n) \)的时间复杂度。下面演示了 `collections.deque` 类的所有操作:
 from collections import deque
 # 创建一个空的双端队列
 d = deque()
 # 添加元素到队列右端
 d.append('R1')
 d.append('R2')
 # 添加元素到队列左端
 d.appendleft('L1')
 d.appendleft('L2')
 # 查看队列中的元素
 print("双端队列:", d)
 # 从队列右端移除元素并返回
 r_pop = d.pop()
 print("从右端弹出:", r_pop)
 # 从队列左端移除元素并返回
 l_pop = d.popleft()
 print("从左端弹出:", l_pop)
 # 创建具有多个元素的双端队列
 d = deque('abcdefg')
 # 限制双端队列的大小,当限制大小时,新添加的元素会挤出对立面的元素
 d_max = deque(maxlen=3)
 d_max.append(1)
 d_max.append(2)
 d_max.append(3)
 d_max.append(4) # 此时1会被挤出队列
 print("具有最大长度限制的双端队列:", d_max)
 # 计数元素出现的次数
 count_a = d.count('a')
 print("'a'的出现次数:", count_a)
 # 扩展双端队列右边,等同于逐个添加
 d.extend('hijk')
 print("右边扩展后:", d)
 # 扩展双端队列左边,等同于逐个添加到左边
 d.extendleft('1234')
 print("左边扩展后:", d)
 # 查找元素的索引,可以指定起始和结束的搜索位置
 index_of_d = d.index('d', 0, len(d))
 print("'d'的索引:", index_of_d)
 # 在双端队列中插入元素到指定位置
 d.insert(3, 'inserted')
 print("插入元素:", d)
 # 移除首次出现的特定元素
 d.remove('a')
 print("移除'a':", d)
 # 反转双端队列
 d.reverse()
 print("反转后:", d)
 # 清空双端队列
 d.clear()
 print("清空后:", d)
 #以上列举了 `collections.deque` 的所有常用操作。原理上,`deque` 是通过双向链表实现的,因此它在两端的添加和移除操作中表现出更好的效率。
 # 需要注意的是,尽管在两端的操作上很快,但是在双端队列的中间进行操作(如插入或删除)则并不比列表高效。
Deque()
#计数器############################
def Counter():
 #计数器(Counter)是 `collections` 模块下的一个字典子类,用于计数可哈希对象。它的元素是存储为字典的键,而它们的计数是存储为字典值。
 #下面我将演示 `collections.Counter` 类的所有操作:
 from collections import Counter
 # 创建一个计数器对象
 c = Counter(['a', 'b', 'c', 'a', 'b', 'b'])
 # 更新计数器
 c.update(['a', 'b', 'c', 'a'])
 # 查看元素的计数
 print("元素计数:", c)
 # 获取计数器中最常见的n个元素
 most_common = c.most_common(2)
 print("最常见的两个元素及其计数:", most_common)#相同时,序号靠前的优先
 # 获取所有元素
 elements = list(c.elements())
 print("所有元素:", elements)
 # 直接使用字典语法访问元素的计数
 print("元素'a'的计数:", c['a'])
 # 设置计数值
 c['a'] = 5
 print("设置'a'的计数为5:", c)
 # 获取元素计数的总数
 total_elements = sum(c.values())
 print("元素总计数:", total_elements)
 # 转化为列表
 list_conv = list(c)
 print("转换为列表:", list_conv)
 # 转化为集合
 set_conv = set(c)
 print("转换为集合:", set_conv)
 # 转化为常规字典
 dict_conv = dict(c)
 print("转换为字典:", dict_conv)
 # 转换为键值对列表
 kv_pairs = c.items()
 print("键值对列表:", kv_pairs)
 # 清空计数器
 c.clear()
 print("计数器清空:", c)
 # 计算相减
 c.subtract(Counter(['a', 'b', 'c', 'b', 'd']))
 print("计数器相减后:", c)
 #计数器相减后: Counter({'a': -1, 'c': -1, 'd': -1, 'b': -2})
 # 计数器之间的运算(加法)
 c1 = Counter(a=3, b=1)
 c2 = Counter(a=1, b=2)
 c = c1 + c2
 print("计数器相加后:", c)
 #计数器相加后: Counter({'a': 4, 'b': 3})
 # 计数器之间的运算(减法)
 c = c1 - c2
 print("计数器相减后:", c)
 #计数器相减后: Counter({'a': 2})(只保留正计数结果)
 # 计数器之间的运算(交集)
 c = c1 & c2
 print("计数器交集后:", c)
 #计数器交集后: Counter({'a': 1, 'b': 1})
 # 计数器之间的运算(并集)
 c = c1 | c2
 print("计数器并集后:", c)
 #不支持环合
 #计数器并集后: Counter({'a': 3, 'b': 2})
 #以上列举了 `Counter` 类的所有常用操作和方法。`Counter` 的原理基本上是对字典的扩展和增强,通过计数功能,
 # 使得对具有重复值的数据的操作更加高效和方便。它的大部分方法都是专为计数而设计的,但它仍然保留字典的大部分常见属性和方法。
Counter()
#有序字典####################################
def OrderedDict():
 #有序字典(OrderedDict)是Python中collections模块提供的一个字典的子类,它记住了字典对象中元素添加的顺序,
 # 这与一般的字典(在Python 3.7+版本之前)不同,后者是无序的。自Python 3.7起,普通字典也是有序的,
 # 但是OrderedDict类仍提供了一些普通字典没有的功能,比如`popitem`的order参数。
 #下面列出一些有序字典(OrderedDict)最常见的操作方法:
 ### 导入OrderedDict
 from collections import OrderedDict
 ### 创建OrderedDict对象
 ordered_dict = OrderedDict()
 ### 添加元素
 ordered_dict['key1'] = 'value1'
 ordered_dict['key2'] = 'value2'
 ### 删除元素
 del ordered_dict['key1']  # 删除键为'key1'的元素
 ### 清空字典
 ordered_dict.clear()
 ### 重新设置元素值
 ordered_dict['key2'] = 'new_value2'
 ### 获取元素
 value = ordered_dict['key2']
 ### 检查键是否存在
 'key2' in ordered_dict
 ### 删除并返回一个元素
 key, value = ordered_dict.popitem(last=True)  # True表示返回最后一个添加进的元素,False表示第一个
 ### 移除指定元素,找不到报KeyError: 'key2'
 ordered_dict['key2'] = 'value2'
 ordered_dict.pop('key2')
 ### 获取所有键
 keys = list(ordered_dict.keys())
 ### 获取所有值
 values = list(ordered_dict.values())
 ### 访问所有键值对
 for key, value in ordered_dict.items():
     print(key, value)
 ### 接收常规字典并保持其顺序
 regular_dict = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
 ordered_dict = OrderedDict(sorted(regular_dict.items(), key=lambda t: t[0]))
 ### 移动一个元素到字典的末尾
 ordered_dict['key2'] = 'value2'
 ordered_dict.move_to_end('key2', last=True)  # True表示移动到末尾,False表示移动到开头
 ### 更新字典元素(类似字典update,但保持有序)
 ordered_dict.update({'key3': 'value3'})
 #这些操作基本覆盖了`OrderedDict`的所有方法。由于`OrderedDict`遵循Python的字典类的所有接口,因此它支持与普通字典几乎相同的所有操作,
 # 但关键之处在于它会按照元素加入的顺序来保持键值对的顺序。
#OrderedDict()
#默认字典######################################
def Defaultdict():
 #默认字典(`defaultdict`)在Python的`collections`模块中是一个非常有用的数据结构,它扩展了常规字典(`dict`)的功能。
 # 其原理是在创建时可以给定一个默认工厂函数,当你尝试访问一个不存在的键时,默认字典将自动为这个键创建一个值,这个值是通过调用工厂函数得到的。
 # 下面是`defaultdict`的通常操作和原理演示:
 # 1. 创建`defaultdict`:
 # 首先导入`collections`模块,并定义一个默认工厂函数(例如,创建列表、整数、集合等)。这里我们使用列表作为默认值。
 from collections import defaultdict
 # 当访问不存在的键时,将创建一个默认值空列表[]
 default_dict = defaultdict(list)
 #2. 添加元素:
 # 可以向`defaultdict`添加元素,类似于常规字典。
 # 添加元素
 default_dict['key1'].append(1)#每个默认元素都是一个列表,所以append
 default_dict['key2'].append(2)
 print("default_dict ",default_dict)
 #3. 访问字典中的元素:
 # 你可以访问字典中存在的键关联的值,如果键不存在,则会自动创建并赋予工厂函数的默认值。
 print(default_dict['key1'])  # 输出存在的键
 print(default_dict['key3'])  # 输出不存在的键,返回[]并在字典中创建键'key3'
 #4. 更新元素:
 # 可以更新已存在的键对应的值。
 # 更新已存在的键的值
 default_dict['key1'].append(3)
 print(default_dict['key1'])
 #5. 删除元素:
 # 删除元素的操作与常规字典一致,可以使用`del`删除指定键。
 # 删除一个元素
 del default_dict['key2']
 print(default_dict)
 #6. 获得所有的键和值:通过`.keys()`和`.values()`方法可以获取字典的键和值。
 # 获取所有键
 print(default_dict.keys())
 # 获取所有值
 print(default_dict.values())
 #7. 获得所有的键值对:通过`.items()`方法可以获得所有键值对。
 # 获取所有键值对
 print(default_dict.items())
 #8. 检查键是否存在:
 # 可以像常规字典一样检查字典中是否存在某个键。
 # 检查键是否存在
 print('key1' in default_dict)  # 返回True
 print('key4' in default_dict)  # 返回False
 #9. 设置默认值:可以通过`.default_factory`属性来设置或更改默认工厂函数。
 # 更改默认值工厂为int,默认值为0
 default_dict.default_factory = int
 print(default_dict['key4'])
 #10. 清空字典:使用`.clear()`方法可以清空字典中所有元素。
 # 清空字典
 default_dict.clear()
 print(default_dict)
 #这就是`defaultdict`的所有基本操作和原理。它在处理缺失键时非常有用,避免了额外的键存在检查,使代码更加简洁易读。
Defaultdict()

9、 NumPy 

函数总览

通过下面形式执行

arr = np.array([6, 7, 8, 9])

x = np.searchsorted(arr, 7)

创建数组:array([1, 2, 3, 4], ndmin=5, dtype='S')创建一个有 5 个维度的数组,给定类型为字符串

返回当前数组指定类型后的副本: arr.astype('i')   把数据类型更改为整数

返回副本:arr.copy()

返回视图:arr.view()

检查数组是否拥有自己的数据:arr.base  副本返回 None。  视图返回原始数组。

数组形状(返回一个元组,每个索引具有相应元素的数量,返回 (2, 4),这意味着该数组有 2 个维,每个维有 4 个元素。):arr.shape

重塑数组形状: arr.reshape(4, 3)最外面的维度将有 4 个数组,每个数组包含 3 个元素

-1用于指代一个未知维数(不能有多个-1),reshape(-1)展平高维为一维

遍历数组每个元素:for x in np.nditer(arr, flags=['buffered'], op_dtypes=['S']):flags=['buffered']启用用于转换的额外空间,op_dtypes给定要转换的类型

枚举迭代数组(需要获取索引):for idx, x in np.ndenumerate(arr):

沿着轴连接数组(堆叠)并返回新数组(如果未显式传递轴,则将其视为 0(行),二维才有轴1(列),高维有更多轴,三维0页1行2列):np.concatenate((arr1, arr2), axis=1) 把arr1[0]和arr2[0]拼、arr1[1]和arr2[1]拼,若axis=0,arr1[0]、arr2[0 ]、arr1[1]、arr2[1]均为新数组元素

沿行堆叠返回新数组:np.hstack((arr1, arr2))

沿列堆叠返回新数组:np.vstack((arr1, arr2))

沿高度堆叠(高度=深度,高度是数组第一维维数,深度是数组最后一维维数)返回新数组:np.dstack((arr1, arr2)) 把arr1[0,0]和arr2[0,0]拼为新[0,0]、arr1[0,1]和arr2[0,1]拼为新[0,1]

沿着新轴连接数组(堆栈)返回新数组:np.stack((arr1, arr2),axis=1) 把arr1[0]和arr2[0]作为新0元素的子元素、arr1[1]和arr2[1]作为新1元素的子元素,若axis=0,arr1[0]和arr1[1]作为新0元素的子元素、arr2[0]和arr2[1]作为新1元素的子元素

尽量平分数组为各个子数组并返回整合后的新数组(不能平分时在末几位调整子数组元素数使可分完):np.array_split(arr, 3, axis=1)沿列把arr分成3部分 把arr[0]分成3部分,作为单独的数组分别归入子数组的0号单位,每个子数组有arr每个子数组的对应的部分

沿列尽量平分数组:np.hsplit(arr, 3)

沿行尽量平分数组:np.vsplit(arr, 3)

沿页尽量平分数组(3维及以上):np.dsplit(arr, 3)  把arr[0,0]分成3部分,作为单独的数组分别归入子数组的0号单位,每个子数组有arr每个子数组的对应的部分

在数组中搜索(检索)某个值,然后返回获得匹配的索引:np.where()

在数组中执行二分搜索(也叫二进制搜索),判断给定值应该插入哪个索引位置并返回索引值:np.searchsorted()

对数组进行升序排序并返回排序后的副本(原数组不变,高维对每个一维成员分别排序):sort()

布尔索引进行过滤(保留True的索引):下面得到[61, 63, 65],还可以filter_arr = arr > 62、filter_arr = arr % 2 == 0得到过滤数组(普通列表不能这样写:NumPy的广播(broadcasting)特性,允许你对整个数组进行条件过滤而不需要显式地使用循环。)

arr = np.array([61, 62, 63, 64, 65])

x = [True, False, True, False, True]

newarr = arr[x]

通过下面形式执行:

from numpy import random

x = random.rand()

生成随机整数:randint(100, size=(3, 5))生成有 3 行的 2-D 数组,每行包含 5 个从 0 到 100 之间的随机整数

生成0到1之间的随机浮点数:rand(3, 5)生成有 3 行的 2-D 数组,每行包含 5 个随机数

生成指定范围内指定个数的随机浮点数列表:np.random.uniform(0.0, 5.0, 250) 生成0到5内250个随机浮点数

生成指定范围内指定个数的随机正态分布数列表(100000个均值为5,标准差为1的随机正态分布数):x = np.random.normal(5.0, 1.0, 100000)

随机返回给定数组中的一个值:choice([3, 5, 7, 9], size=(3, 5))生成由数组参数(3、5、7 和 9)中的值组成的二维数组

ufunc通用函数:进行科学计算

ufunc 用于在 NumPy 中实现矢量化,这比迭代元素要快得多。

它们还提供广播和其他方法,例如减少、累加等,它们对计算非常有帮助。

ufuncs 还接受其他参数,比如:

where 布尔值数组或条件,用于定义应在何处进行操作。

dtype 定义元素的返回类型。

out 返回值应被复制到的输出数组。

通过下面形式执行:返回[ 5  7  9 11]

import numpy as np

x = [1, 2, 3, 4]
y = [4, 5, 6, 7]
z = np.add(x, y)

两个数组对应每位相加,返回加法结果:add()

求并返回均值:

speed = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86])
x = np.mean(speed)

求并返回中值:np.median(speed)

求并返回众数及频次类:

from scipy import stats
x = stats.mode(speed)
print(x.mode,x.count)

求标准差:x = np.std(speed)

求方差:np.var(speed)

求并返回百分位数(数组中刚好75%的数小等于它的数):np.percentile(speed,75)

绘制并显示数组的直方图,指定直方图含有5栏(5根柱子):

import matplotlib.pyplot as plt
plt.hist(speed, 5)
plt.show()

绘制并显示指定x、y坐标下的散点图:

x = [5,7,8,7,2,17,2,9,4,11,12,9,6]
y = [99,86,87,88,111,86,103,87,94,78,77,85,86]
plt.scatter(x, y)
plt.show()

计算线性回归(斜率 (slope)、截距 (intercept)、相关系数 (rvalue,r 平方值的范围是 0 到 1,其中 0 表示不相关,而 1 表示 100% 相关。r绝对值太低表示不适合线性回归)、p值 (pvalue) 和标准误差 (stderr)):slope, intercept, r, p, std_err = stats.linregress(x, y)可以直接接收对象的成员

绘制线性回归图(用plt.plot函数绘制折线图):

mymodel = list(map(lambda x:slope * x + intercept, x))
plt.scatter(x, y)
plt.plot(x, mymodel)
plt.show()

多项式建立和运算:np.poly1d([1, -3, 2]) 结果是1 x - 3 x + 2

result_add = poly1 + poly2   result_mul = poly1 * poly2

求值:result_eval = poly(2)

求导:derivative = poly.deriv()

求积分:integral = poly.integ()

多项式拟合:numpy.polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False)

  • x 一维数组,表示自变量的数据点。
  • y 一维数组,表示因变量的数据点。
  • deg 多项式的阶数(次数)。
  • rcond 可选参数,用于控制奇异值的截断。默认值为 None。
  • full 可选参数,如果为 True,则返回额外的信息,包括拟合残差、秩、奇异值等。默认值为 False。
  • w 可选参数,表示每个数据点的权重。默认值为 None。
  • cov 可选参数,如果为 True,则返回拟合系数的协方差矩阵。默认值为 False。

该函数返回一个包含多项式系数的一维数组,其中最高次的系数在数组的开头。例如,对于二次多项式,返回的数组为 [a2, a1, a0]

计算相关系数、指定x处值:

from sklearn.metrics import r2_score
print(r2_score(y, mymodel(x)))计算相关系数
print(mymodel(100))计算100处值

mymodel = numpy.poly1d(numpy.polyfit(x, y, 3))
myline = numpy.linspace(1, 22, 100)生成范围在1到22内的等间隔序列作为x坐标
plt.plot(myline, mymodel(myline))画出回归线

NumPy 旨在提供一个比传统 Python 列表快 50 倍的数组对象。

NumPy 中的数组对象称为 ndarray,它提供了许多支持函数,使得利用 ndarray 非常容易。

0-D 数组,或标量(Scalars),是数组中的元素。数组中的每个值都是一个 0-D 数组。

1-D 数组元素为 0-D 数组的数组,称为一维或 1-D 数组。

2-D 数组元素为 1-D 数组的数组,称为 2-D 数组。它们通常用于表示矩阵或二阶张量。

NumPy 有一个专门用于矩阵运算的完整子模块 numpy.mat

3-D 数组元素为 2-D 数组的数组,称为 3-D 数组。

NumPy 数组提供了 ndim 属性,该属性返回一个整数,该整数会告诉我们数组有多少维。

NumPy 数组中的索引以 0 开头,这意味着第一个元素的索引为 0,第二个元素的索引为 1,以此类推。

使用负索引从尾开始访问数组。 

python 中裁切的意思是将元素从一个给定的索引带到另一个给定的索引。

我们像这样传递切片而不是索引:[startend]

我们还可以定义步长,如下所示:[startendstep]

如果我们不传递 start,则将其视为 0。

如果我们不传递 end,则视为该维度内数组的长度。

如果我们不传递 step,则视为 1。

结果包括了开始索引,但不包括结束索引。

以下是 NumPy 中所有数据类型的列表以及用于表示它们的字符。

  • i - 整数
  • b - 布尔
  • u - 无符号整数
  • f - 浮点
  • c - 复合浮点数
  • m - timedelta
  • M - datetime
  • O - 对象
  • S - 字符串
  • U - unicode 字符串
  • V - 固定的其他类型的内存块 ( void )

NumPy 数组对象有一个名为 dtype 的属性,该属性返回数组的数据类型

如果给出了不能强制转换元素的类型,则 NumPy 将引发 ValueError。

ValueError:在 Python 中,如果传递给函数的参数的类型是非预期或错误的,则会引发 ValueError。

无法将非整数字符串(比如 'a')转换为整数(将引发错误):

import numpy as np

arr = np.array(['a', '2', '3'], dtype='i')

更改现有数组的数据类型的最佳方法,是使用 astype() 方法复制该数组。

astype() 函数创建数组的副本,并允许您将数据类型指定为参数。

数据类型可以使用字符串指定,例如 'f' 表示浮点数,'i' 表示整数等。或者您也可以直接使用数据类型,例如 float 表示浮点数,int 表示整数。

 副本和视图之间的区别

副本和数组视图之间的主要区别在于副本是一个新数组,而这个视图只是原始数组的视图。

副本拥有数据,对副本所做的任何更改都不会影响原始数组,对原始数组所做的任何更改也不会影响副本。

视图不拥有数据,对视图所做的任何更改都会影响原始数组,而对原始数组所做的任何更改都会影响视图。

副本拥有数据,而视图不拥有数据,但是我们如何检查呢?

每个 NumPy 数组都有一个属性 base,如果该数组拥有数据,则这个 base 属性返回 None

否则,base 属性将引用原始对象。

获取数组的形状

NumPy 数组有一个名为 shape 的属性,该属性返回一个元组,每个索引具有相应元素的数量。

元组的形状代表什么?

每个索引处的整数表明相应维度拥有的元素数量。

索引 4,我们的值为 4,因此可以说第 5 个 ( 4 + 1 th) 维度有 4 个元素。

数组重塑(返回视图

重塑意味着更改数组的形状。

数组的形状是每个维中元素的数量。

通过重塑,我们可以添加或删除维度或更改每个维度中的元素数量。

 我们可以重塑成任何形状吗?

是的,只要重塑所需的元素在两种形状中均相等。

我们可以将 8 元素 1D 数组重塑为 2 行 2D 数组中的 4 个元素,但是我们不能将其重塑为 3 元素 3 行 2D 数组,因为这将需要 3x3 = 9 个元素。

尝试将具有 8 个元素的 1D 数组转换为每个维度中具有 3 个元素的 2D 数组(将产生错误):

import numpy as np

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])

newarr = arr.reshape(3, 3)

print(newarr)

未知的维

您可以使用一个“未知”维度。

这意味着您不必在 reshape 方法中为维度之一指定确切的数字。

传递 -1 作为值,NumPy 将为您计算该数字。

 我们不能将 -1 传递给一个以上的维度。

展平数组

展平数组(Flattening the arrays)是指将多维数组转换为 1D 数组。

我们可以使用 reshape(-1) 来做到这一点。

数组迭代

迭代意味着逐一遍历元素。

当我们在 numpy 中处理多维数组时,可以使用 python 的基本 for 循环来完成此操作。

如果我们对 1-D 数组进行迭代,它将逐一遍历每个元素。

 使用 nditer() 迭代数组

函数 nditer() 是一个辅助函数,从非常基本的迭代到非常高级的迭代都可以使用。它解决了我们在迭代中面临的一些基本问题,让我们通过例子进行介绍。

迭代每个标量元素

在基本的 for 循环中,迭代遍历数组的每个标量,我们需要使用 n 个 for 循环,对于具有高维数的数组可能很难编写。

迭代不同数据类型的数组

我们可以使用 op_dtypes 参数,并传递期望的数据类型,以在迭代时更改元素的数据类型。

NumPy 不会就地更改元素的数据类型(元素位于数组中),因此它需要一些其他空间来执行此操作,该额外空间称为 buffer,为了在 nditer() 中启用它,我们传参 flags=['buffered']

使用 ndenumerate() 进行枚举迭代

枚举是指逐一提及事物的序号。

有时,我们在迭代时需要元素的相应索引,对于这些用例,可以使用 ndenumerate() 方法。

 连接 NumPy 数组

连接意味着将两个或多个数组的内容放在单个数组中。

在 SQL 中,我们基于键来连接表,而在 NumPy 中,我们按轴连接数组。

我们传递了一系列要与轴一起连接到 concatenate() 函数的数组。如果未显式传递轴,则将其视为 0。

arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
arr = np.hstack((arr1, arr2))
NumPy 提供了一个辅助函数:hstack() 沿行堆叠。[[1 2 5 6][3 4 7 8]]
NumPy 提供了一个辅助函数:vstack() 沿列堆叠。[[1 2][3 4][5 6][7 8]]
NumPy 提供了一个辅助函数:dstack() 沿高度堆叠,该高度与深度相同。[[[1 5][2 6]][[3 7][4 8]]]
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17, 18]])
newarr = np.hsplit(arr, 3)
print(newarr)#[array([[ 1],[ 4],[ 7],[10],[13],[16]]), array([[ 2],[ 5],[ 8],[11],[14],[17]]), array([[ 3],[ 6],[ 9],[12],[15],[18]])]
newarr = np.vsplit(arr, 3)
print(newarr)#[array([[1, 2, 3],[4, 5, 6]]), array([[ 7,  8,  9],[10, 11, 12]]), array([[13, 14, 15],[16, 17, 18]])]
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
newarr = np.dsplit(arr, 3)
print(newarr)#[array([[[ 1],[ 4]],[[ 7],[10]]]), array([[[ 2],[ 5]],[[ 8],[11]]]), array([[[ 3],[ 6]],[[ 9],[12]]])]

使用堆栈函数连接数组

堆栈与级联相同,唯一的不同是堆栈是沿着新轴完成的。

我们可以沿着第二个轴连接两个一维数组,这将导致它们彼此重叠,即,堆叠(stacking)。

我们传递了一系列要与轴一起连接到 concatenate() 方法的数组。如果未显式传递轴,则将其视为 0。

拆分 NumPy 数组

拆分是连接的反向操作。

连接(Joining)是将多个数组合并为一个,拆分(Spliting)将一个数组拆分为多个。

我们使用 array_split() 分割数组,将要分割的数组和分割数传递给它。

array_split() 方法的返回值是一个包含每个分割的数组。

如果将一个数组拆分为 3 个数组,则可以像使用任何数组元素一样从结果中访问它们

如果数组中的元素少于要求的数量,它将从末尾进行相应调整。

我们也有 split() 方法可用,但是当源数组中的元素较少用于拆分时,它将不会调整元素

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值