标识符命名规则
| 类型 | 规则 | 例子 |
|---|---|---|
| 模块和包名 | 全是小写字母, 若多个单词之间用下划线 | math, sys, os |
| 函数名 | 全是小写字母, 若多个单词之间用下划线 | add, update |
| 类名 | 首字母大写, 采用驼峰命名法, 多个单词时, 每个单词首字母大写 | MyPhone |
| 常量名 | 全是大写字母, 多个单词使用下划线隔开 | MAX_SPEED |
变量和对象
- 变量没有类型, 但是引用类型(对象id地址), 存放在栈内存stack
- 对象是有类型的, 存放在堆内存heap
时间表示
计算机中时间的表示是从"1970年1月1日00:00:00"开始, 以毫秒(1/1000)秒进行计算, 这个时刻称为"unix时间点"
python中可以通过time.time()获得当前时刻, 返回的值以秒为单位, 带微妙(1/1000)毫秒精度的浮点值, 例如:1693407364.4558322
可变字符串
- python中, 字符串属于不可变对象, 不支持原地修改, 如果需要修改其中的值, 只能创建新的字符串对象
- 确实需要原地修改字符串, 可以使用io.StringIO对象或者array模块
import io
a = "123456"
# print(id(a))
# a = "123789"
# print(id(a))
b = io.StringIO(a)
print(b.getvalue())
print(id(b))
b.seek(3)
b.write("***")
print(b.getvalue())
print(id(b))
序列
序列是一种数据存储方式, 用于存储一系列的数据, 在内存中, 序列就是一块用来存放多个值的连续的内存空间
列表效率问题
当列表增加和删除元素时, 列表会自动进行内存管理, 大大减少了程序员的负担, 但是这个特点涉及列表元素的大量移动, 效率较低
除非必要, 一般只在列表的尾部添加元素或者删除元素, 这会大大提高列表的操作效率
append()方法: 速度最快, 推荐使用
+运算符操作: 不是尾部添加元素, 而是创建新的列表对象, 将原列表的元素和新列表的元素依次复制到新的列表对象中, 这样, 会涉及到大量的复制操作, 对于操作大量元素不建议使用
a = [10, 20]
print(id(a))
a = a + [30]
print(id(a)) # 两次地址不一样, 创建了新的对象
extend()方法: 把目标列表的所有元素添加到本列表的尾部, 属于原地操作, 不创建新的列表对象
insert()插入元素方法:使用insert()方法课将指定的元素插入到列表对象的任意指定位置, 这样会让插入位置后面所有的元素进行移动, 会影响到处理速度
乘法操作: 生成新对象
函数内存分析
函数也是对象
函数运行在栈内存stack, 创建对象在堆内存heap
Python中, 圆括号意味着调用函数, 在没有圆括号的情况下, Python会把函数当作普通对象
全局变量和局部变量
全局变量
- 在函数和类定义之外声明的变量, 作用域为定义的模块, 从定义位置开始直到模块结束
- 全局变量降低了函数的通用性和可读性, 应尽量避免全局变量的使用
- 要在函数内改变全局变量的值, 使用global声明一下
局部变量
- 在函数体中(包括形式参数)声明的变量
- 局部变量的引用比全局变量快, 优先考虑使用
- 如果局部变量和全局变量同名, 则在函数内隐藏全局变量, 只使用同名的局部变量
新函数
a = 100
def f1():
global b
b = 33
a = 11
c = 22
print(locals()) #打印输出的局部变量
print(globals()) #答应输出的全局变量
print(a)
f1()
效率问题代码演示
import time
a = 1000
def demo01():
start = time.time()
global a
for i in range(100000000):
a += 1
end = time.time()
t = end - start
print(f"耗时{t}")
def demo02():
start = time.time()
c = 1000
for i in range(100000000):
c += 1
end = time.time()
t = end - start
print(f"耗时{t}")
demo01()
demo02()
参数传递
函数的参数传递本质就是: 从实参到形参的赋值操作, Python中"一切皆对象", 所有的赋值操作都是"引用的赋值", 所以, Python中的参数的传递都是"引用传递", 不是"值传递"
具体操作时分为两类:
- 对"可变对象"进行"写操作", 直接作用于原对象本身, 其中"可变对象"有: 字典, 列表, 集合, 自定义的对象等
- 对"不可变对象"进行"写操作", 会产生一个新的"对象空间", 并用新的值填充这块空间, 其中"不可变对象"有: 数字, 字符串, 元组, function等
注意:
- 传递不可变对象时, 不可变对象里面包含的子对象是可变的话, 如果方法内修改了这个可变对象, 则源对象也会发生变化
浅拷贝和深拷贝
内置函数
- copy(浅拷贝): 拷贝对象, 但不拷贝子对象的内容, 只是拷贝子对象的引用
- deepcopy(深拷贝): 拷贝对象, 并且会连子对象的内容也会全部(递归)拷贝一份, 对子对象的修改不会影响源对象
import copy
def demo_copy():
a = [11, 22, [33, 44]]
b = copy.copy(a)
print(a)
print(b)
b.append(55)
b[2].append(66)
print(a)
print(b)
demo_copy()
print("-----------------")
def demo_deepcopy():
a = [11, 22, [33, 44]]
b = copy.deepcopy(a)
print(a)
print(b)
b.append(55)
b[2].append(66)
print(a)
print(b)
demo_deepcopy()
参数类型: 可变参数
可变参数指的是"可变数量的参数"
- *param(一个星号), 将多个参数收集到一个"元组"对象中
- **param(两个星号), 将多个参数收集到一个"字典"对象中
强制命名参数
在带星号的"可变参数"后面增加新的参数, 必须在调用的时候"强制命名参数"
lambda表达式和匿名函数
lambda表达式可以用来声明匿名函数, lambda函数是一种简单的, 在同一行中定义函数的方法
lambda函数实际生成了一个函数对象
lambda表达式只允许包含一个表达式, 不能包含复杂语句, 该表达式的计算结果就是函数的返回值
f = lambda a, b, c: a + b + c
print(f(1, 2, 3))
eval()函数
功能: 将字符串str当成有效的表达式来求值并返回计算结果
递归函数
基本思想: 自己调用自己
- 终止条件: 表示递归什么时候结束, 一般用于返回值, 不再调用自己
- 递归步骤: 把第n步的值和第n-1步相关联
def f(n):
print("start:" + str(n))
if n == 1:
print("recuesion over!")
else:
f(n - 1)
print("end:" + str(n))
def f1(n):
if n == 1:
return 1
else:
return n * f1(n-1)
嵌套函数
- 定义: 在函数内部定义的函数
- 一般在什么情况下使用嵌套函数?
- 封装 - 数据隐藏(外部无法访问嵌套函数)
- 贯彻DRY(dont repeat yourself)原则
- 嵌套函数, 可以让我们在函数内部避免重复代码
- 闭包
def printName(isChinese, name, familyName):
def inner_print(a, b):
print(f"{a} {b}")
if isChinese:
print(familyName, name)
else:
inner_print(name, familyName)
printName(True, "三", "张")
printName(False, "Geroge", "Bush")
nonlocal和global关键字
nonlocal: 用来在内部函数中, 声明外层的局部变量
global: 函数内部声明全局变量, 然后才使用全局变量
a = 100
def outer():
b = 200
def inner():
nonlocal b
print("inner", b)
b = 300
global a
a = 400
inner()
print("outer", b)
outer()
print(a)
LEGB规则
Python在查找"名称"时, 是按照LEGB规则查找的:
Local > Enclosed > Global > Bulit in
Local: 指的就是函数或者类的方法内部
Enclosed: 值得是嵌套函数(一个函数包裹另一个函数, 闭包)
Global: 指的是模块中的全局变量
Bulit in: 指的是Python为自己保留的特殊名称
字典核心底层原理
字典对象的核心是散列表, 散列表是一个稀疏数组(总是有空白元素的数组), 数组的每个单元叫做bucket(桶), 每个bucket有两部分: 一个是键对象的引用, 一个是值对象的引用
由于, 所有bucket结构和大小一致, 我们可以通过偏移量来读取指定bucket
扩容
- python会根据散列表的拥挤程度扩容, "扩容"指的是: 创造更大的数组, 将原有内容拷贝到新数组中
- 接近2/3时, 数组就会扩容
用法总结
- 字典在内存中开销巨大, 典型的空间换时间
- 键查询速度很快
- 往字典里面添加新键可能导致扩容, 导致散列表中键的次序变化, 因此, 不要在遍历字典的同时进行字典的修改
- 键必须可散列
- 数字, 字符串, 元组, 都是可散列的
- 自定义对象需要支持下面三点
- 支持hash()函数
- 支持通过_eq_()方法检测相等性
- 若a==b为真, 则hash(a)==hash(b)也为真
集合
集合是无序可变, 元素不可重复, 实际上, 集合底层是字典实现, 集合的所有元素是字典中的键对象, 因此是不能重复且唯一的
集合创建和删除: add set remove
集合相关操作: 并集, 交集, 差集