数据类型
- 整型:int
- 浮点型:小数,包括一般数学写法(1.234)和科学计数法(1.234e3)
- 字符串型:字符串是以单引号或双引号括起来的任意文本,比如’hello’和"hello",字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法,而且可以书写成多行的形式(用三个单引号或三个双引号开头,三个单引号或三个双引号结尾)。
- 布尔型:bool,布尔值分为True和False
- 复数型:形如1+2j.
变量命名
- 硬性规则:
- 变量名由字母(广义的Unicode字符,不包括特殊字符)、数字和下划线构成,数字不能开头。
- 大小写敏感(大写的a和小写的A是两个不同的变量)。
- 不要跟关键字和系统保留字(如函数、模块等的名字)冲突。
- PEP 8要求:
- 用小写字母拼写,多个单词用下划线连接。
- 受保护的实例属性用单个下划线开头。
- 私有的实例属性用两个下划线开头。
Python内置的数据类型转换
- int():将数值或字符串变量转换为整型,可以指定进制
- float():将字符串变量转为浮点型
- str():将指定的变量转换为字符串类型,可以指定编码
- chr():将整型变量转换为该编码对应的字符串(一个字符)
- ord():将字符串变量(一个字符)转换为对应的编码(整型)
运算符
Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符:
运算符 | 描述 |
---|---|
[]、[:] | 下标,切片 |
** | 指数 |
~、+、- | 按位取反, 正负号 |
*、/、%、// | 乘,除,模,整除 |
+、- | 加,减 |
>>、<< | 右移,左移 |
& | 按位与 |
^、| | 按位异或,按位或 |
<=、<、>、>= | 小于等于,小于,大于,大于等于 |
==、!= | 等于,不等于 |
is、is not | 身份运算符 |
in、not in | 成员运算符 |
not、or、and | 逻辑运算符 |
=、+=、-=、*=、/=、%=、//=、**=、&=、 =、^=、>>=、<<= |
函数
参数种类
Python函数的参数可以分为默认参数、位置参数、关键字参数、可变参数。
- 默认参数:定义函数时,为形参提供默认值(param=10),默认参数必须位于最右端。调用函数时如果没有传入对应实参,则取默认参数
- 位置参数:调用函数时传入实参的数量和位置必须和定义函数时一致
- 关键字参数:调用函数时采用键值对的方式(key=value)。混合位置和关键字传参时,关键字参数必须在位置参数后。
- 可变参数
位置参数与关键字参数
可以使用 / 和 * 来指定参数类型,如下所示:
def f(pos1, pos2, /, pos_or_pwd, *, kwd1, kwd2): pass
# 此处 pos1和 pos2 仅限位置参数;pos_or_pwd 是位置或关键字参数;pwd1 和 pwd2 仅限关键字参数
可变参数 *args 和 ** kwargs
- *args:可接受任意个位置参数,当调用函数时,所有未匹配的位置参数会在函数内打包为一个 tuple 对象,并赋值给变量 args
- **kwargs:可接受任意个关键字参数,当调用函数时,所有未匹配的关键字参数会在函数内打包为一个 dict 对象,并赋值给变量 kwargs
变量的作用域
Python查找变量时按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索
数据结构
列表 list [ ]
列表生成式 :用于生成列表,格式为 [for x in range(1, 20)]
生成器
- 为什么有生成器:生成器继承于迭代器,因此生成器拥有迭代器的所有特性。而列表和列表生成式中的数据都保存在内存中,当列表中的元素巨大时,将占用大量内存,从而导致内存溢出。
- 生成器是一个对象,没有保存列表中的数据,而是保存了产生元素的算法,同时记录游标的位置来确定当前输出到哪个元素了。每次调用都会返回一个元素,可以通过 next() 方法获取,这种方式既有列表的优势,又不占用空间。
- 格式1 — 类似于列表生成式,将 [ ] 替换为 ( )
g = (x for x in range(1, 10))
print(type(g)) # <class 'generator'>
print(sys.getsizeof(g)) # 112
- 格式2 — 使用 yield 关键字:函数包含了 yield 后便成为了生成器,当调用函数时不会执行内部代码,而是返回一个生成器对象。
def test(number):
a = 0
b = 1
n = 0
while (n < number):
yield b
a, b = b, (a+b)
n += 1
# 此处 yield 关键字表示返回后面的变量b给生成器,而不是返回给函数。
g = test(10) # 此时 g 便是一个生成器对象
- 如何遍历生成器的元素:
- next(生成器对象)
- for循环:for val in g: print(val)
- 生成器对象.next():内置的 __next__,当已经遍历到对象末尾时,会抛出StopIteration异常
- 生成器对象的方法:
- .close():关闭生成器
- .send():和 next() 一样可以用来生成数据,但可以往生成器内部传递数据(可以和生成器内部进行交互)。执行next()和send()时都会在yield处暂停,并返回后边的值。使用send()前必须至少调用一次next()来生成一次数据(或是启动生成器),使生成器内部停留于yield处。而send()向生成器内部传递的数据将在yield处赋值,将传递进去的参数作为被挂起的yield语句的返回值,示例如下:
def writer():
while True:
data = yield
print("data: ", data)
def producer():
it = writer() # 返回生成器
it.__next__()
for i in range(5):
print("send data: %d" %i)
it.send("%d" %i)
producer()
数据类型可变性
- 可变数据类型:list、set、dict
- 不可变数据类型:number (int、float、bool)、string、tuple
python深拷贝与浅拷贝
赋值 =
赋值号 =:仅仅是原对象的一个引用
a = [1, 2, 3]
b = a
print(id(a), " ", id(b))
print("a is b ? ", a is b)
# 139708947438600 139708947438600
# a is b ? True
浅拷贝
重新分配一块内存来创建一个新的对象,但存储的内容是原对象的各个子对象的引用。由此可以看出,当原对象的子对象是可变数据类型时,浅拷贝可能会出现问题。
浅拷贝实现方式:
- 使用数据类型本身的构造器
- 对于列表,还可以使用切片来实现
- 使用 copy.copy() 函数,适用于任何数据类型(tuple除外)
# 1. 使用数据类型本身的构造器: list、set、dict 均适用
list1 = [1, 2, 3]
list2 = list(list1)
print(list2)
print("list1 is list2 ?",list1 is list2)
print("list1==list2 ?",list1==list2)
#### 输出结果
[1, 2, 3]
list1 is list2 ? False # 分配了一块新内存,故与原对象的内存不同
list1==list2 ? True # 新内存存储的元素是原对象的子对象的引用
# 2. 对于列表,可以使用切片操作
list1 = [1, 2, 3]
list2 = list1[:]
print(list2)
print("list1 is list2 ?",list1 is list2)
print("list1==list2 ?",list1==list2)
#### 输出结果
[1, 2, 3]
list1 is list2 ? False
list1==list2 ? True
# 3. 使用 copy.copy() 函数,适用于任何数据类型
import copy
list1 = [1, 2, 3]
list2 = copy.copy(list1)
print(list2)
print("list1 is list2 ?",list1 is list2)
print("list1 == list2 ?",list1 == list2)
#### 输出结果
[1, 2, 3]
list1 is list2 ? False
list1==list2 ? True
# 4. 对于 元组tuple 和 字符串string,使用上述方法不会发生浅拷贝,而与赋值类似,开辟新的内存仅存储原对象的引用,
# 不是对原对象的各个子对象的引用,不属于浅拷贝。
tuple1 = (1, 2, 3)
tuple2 = tuple(tuple1)
print(tuple2)
print("tuple1 is tuple2 ?",tuple1 is tuple2)
print("tuple1 == tuple2 ?",tuple1 == tuple2)
tuple1 = (1, 2, 3)
tuple2 = tuple1[:]
print("tuple1 is tuple2 ?",tuple1 is tuple2)
print("tuple1 == tuple2 ?",tuple1 == tuple2)
tuple1 = (1, 2, 3)
tuple2 = copy.copy(tuple1)
print("tuple1 is tuple2 ?",tuple1 is tuple2)
print("tuple1 == tuple2 ?",tuple1 == tuple2)
#### 输出结果
(1, 2, 3)
tuple1 is tuple2 ? True
tuple1 == tuple2 ? True
# 5. 当浅拷贝时,如果原对象的元素是可变的,则可能会出现问题
list1 = [[1, 2], (30, 40)]
list2 = list(list1)
list1.append(100) # 对list1整体操作,不影响list2内部元素
print("list1:",list1)
print("list2:",list2)
list1[0].append(3) # 对list1内部元素操作,而操作对象[1, 2]是可变的,因此list2会受到影响
print("list1:",list1)
print("list2:",list2)
list1[1] += (50, 60) # 对list1内部元素操作,但操作对象(30, 40)是不可变的,因此list2不会受到影响
print("list1:",list1)
print("list2:",list2)
#### 输出结果
list1: [[1, 2], (30, 40), 100]
list2: [[1, 2], (30, 40)]
list1: [[1, 2, 3], (30, 40), 100]
list2: [[1, 2, 3], (30, 40)]
list1: [[1, 2, 3], (30, 40, 50, 60), 100]
list2: [[1, 2, 3], (30, 40)]
深拷贝
重新开辟一块内存,将原对象中各个子对象以递归的方式,通过创建新子对象来逐一拷贝到新内存中。新创建的对象与原对象无任何关联。
通常使用 copy.deepcopy() 函数实现
import copy
list1 = [[1, 2], (30, 40)]
list2 = copy.deepcopy(list1)
list1[0].append(3) # 对list1内部可变元素[1, 2]操作,但深拷贝下list2不会受到影响
print("list1:",list1)
print("list2:",list2)
#### 输出结果
list1: [[1, 2, 3], (30, 40)]
list2: [[1, 2], (30, 40)]