自己以前整理的笔记,不太完整,后续会不断更新。。。。
- [ ] LEGB规则
- [ ]
一、代码规范
1.# 号注释时在其后跟一个空格,解释器遇到#号直接跳过,不运行
2.代码短的情况下,可将注释放在代码后,但注释要与代码至少间隔2个空格
3.''' '''多行注释/块注释,内容全部忽略
4.代码注释不少越多越好,复杂的操作要加上若干行注释,不要描述代码
5.PEP8对Python的代码格式给出了建议
6.赋值语句=左右两边各保留一个空格
7.变量命名:多个字母时用_连接,且都用小写字母--推荐
8.另外一种命名规则--驼峰命名法:大驼峰、小驼峰
9.代码中的缩进tab和空格不要混用
10.模块命名要求与变量命名要求一致
二、运行原理
- 程序运行完后为程序分配的内存空间清零,数据清除
- 主程序中变量名第一次出现才是定义变量,后续再次出现是直接使用变量
- 程序运行的三大流程:顺序,分支,循环
三、变量
01.变量的引用
- 变量和数据是分开存储的
- 数据保存在内存中某个位置,通过地址找到
- 变量保存着数据在内存中的地址
- 变量中记录数据的地址叫引用
_xx:单前置下划线变量,当作为模块导入其他程序中时,该变量不可访问
__xx:双前置下划线变量,私有属性或方法,子类无法继承和使用
__xx__:前后双下划线,魔法方法或属性
xx_:单后置下划线,用于避免与python关键词冲突
变量作用域
LEGB规则
使用id( )函数可查看变量中所保存的数据的地址,十进制输出
python中函数的 实参/返回值 都是是靠 引用 来传递来的
02.可变类型数据和不可变类型数据
- 不可变数据:数字、字符串、元组
- 可变数据:列表、字典、集合
Tips:
- 字典的key只能是不可变数据,内部进行了hash(key)运算,输入的key必须是不可变类型的数据
- 可变数据的内容变化通过方法实现
- 如果给一个可变数据类型的变量重新赋值相同类型的数据,引用会修改
# 两个a的id不同
a = [1,2]
print(id(a))
a = [1,2]
print(id(a))
# b,c的id相同
b = 1
print(id(b))
c = 1
print(id(c))
03.局部变量和全局变量
x = 1
def func(x):
print('x is ', x)
x = 2
print('local x is', x)
func(x)
print('global x is', x)
# 输出
# x is 1
# local x is 2
# global x is 1
- 全局变量
为了保证所有的函数都能够正确使用到全局变量,应该 将全局变量定义在其他函数的上方
全局变量 是在 函数外部定义 的变量(没有定义在某一个函数内),所有函数 内部 都可以使用这个变量,但不能使用=修改 ,可以使用方法进行修改
若要在函数内修改全局变量,需要先对变量进行global声明 ,再进行重新赋值
x = 1
def func():
# print(x) # 用global 定义全局变量前不能调用,否则报错
global x
print('x is ', x)
x = 2
print('local x is', x)
func()
print('global x is', x)
- 局部变量
函数执行结束后,函数内部的局部变量,会被系统回收
- 若函数内的局部变量名与全局变量名相同,且进行了赋值操作,实质上是新建了一个变量,只不过和全局变量名相同而已
- 其他注意事项见5-2
局部变量的生命周期
- 所谓 生命周期 就是变量从 被创建 到 被系统回收 的过程
- 局部变量 在 函数执行时 才会被创建
- 函数执行结束后 局部变量 被系统回收
- 局部变量在生命周期 内,可以用来存储 函数内部临时使用到的数据
注意:
函数执行时,需要用到变量时 会:
- 首先查找函数内部 是否存在 指定名称 的局部变量,如果有,直接使用
- 如果没有,查找 函数外部 是否存在 指定名称 的全局变量,如果有,直接使用
- 如果还没有,程序报错!
全局变量与局部变量之间的一些注意事项:
- 在 global 中出现的名字不能在global 之前的代码中使用。
- 在 global 中出现的名字不能作为形参, 不能作为循环的控制对象, 不能在类定义, 函数定义, import语句中出现。
hehe=6
def f():
print(hehe) # 不能在赋值前调用 why?????
hehe=2
f()
print(hehe)
# 报错:UnboundLocalError: local variable 'hehe' referenced before assignment
x = 1
def func():
print(x) # 用global 定义全局变量前不能调用,否则报错 why?????????????
global x
print('x is ', x)
x = 2
print('local x is', x)
func()
# 报错:SyntaxError: name 'x' is used prior to global declaration
四、循环
- 使用continue时要注意修改判断条件
- break和continue只对当前循环有效
-
五、函数
- 封装
- 重用
- 函数定义后不调用则不执行
- 定义函数体上方应和其他代码(包括注释)保留两行空行
函数中变量的生命周期????????
5-1 函数参数类型
1.缺省参数/默认参数
- 一般放在形参列表的最后面
- 定义函数时,位置参数要放在默认/缺省参数前,否则语法错误
- 默认参数必须是不可变对象,若是可变对象则会出现问题
如默认参数为列表、字典或大多数类的实例,函数在后续调用中会记录前面传递的参数
def f(a, L = []):
L.append(a)
return L
print(f(1)) # [1]
print(f(2)) # [1,2]
print(f(3)) # [1,2,3]
# 改善方法
def fun(a, li=None):
if li is None:
li = []
li.append(a)
return li
默认参数在函数定义时就被解析,如下:
i = 5
def f(arg=i):
print(arg)
i = 6
f()
# 结果:
# 5
2.可变参数/多值参数/收集参数
函数接收的参数个数不确定时,使用多值参数
- 放在位置参数后,默认参数/关键字参数前
- 若定义函数时,形参位置没有按照上条执行,也可通过关键字参数来赋值
两种类型:
-
形参名前加*,接收为元组
传入的数据被打包成元组,但不包含赋值映射对 -
形参名前加**,接收为字典
将传入的key=value值打包成字典
def demo1(num, *args, **kwargs):
print(num)
print(args)
print(kwargs)
demo1(1, 2, 3, 4, n=1, m=2, st='hello')
# 运行结果
# 1
# (2, 3, 4)
# {'n': 1, 'm': 2, 'st': 'hello'}
拆包
一个问题:若某变量中保存有一个列表或字典,该如何将其内容传入函数?
def demo2(num, *args, **kwargs):
print(num)
print(args)
print(kwargs)
a = (1, 2, 3, 4)
b = {'name': 'xm', 'age': 18}
demo2(1, a, b)
# a,b都传入了args中,kwargs为空
# 1
# ((1,2,3,4),{'name': 'xm', 'age': 18})
# {}
以上程序并没有达到预期,这时需要用到拆包:
在对应的实参名前加上对应的*即可
# 其效果类似于关键字参数,只不过是通过*来识别
def demo3(num, *args, **kwargs):
print(num)
print(args)
print(kwargs)
a = [1, 2, 3, 4]
b = {'name': 'xm', 'age': 18}
demo3(1, *a, **b)
# 1
# (1,2,3,4)
# {'name': 'xm', 'age': 18}
总结:!!!!!!!
以下结论也适用于**,只不过1.**后的数据类型对应的是映射对象(map),2.将映射对象保存在字典中
def f(**a):
print(a)
f(m=1, n=2)
# {'m':1,'n':2}
所以,*和**其实可以起到过滤数据的作用,*后的数据类型必须是可迭代对象,**后的数据类型必须是映射对象;而当**kwargs作为形参接收数据时,可传入的数据类型必须是map对象,而*args没有限制。
1.可迭代对象前加*的效果
# *后跟可迭代对象,结果是将可迭代对象拆开为单个元素
print(1, 2, 3)
# 1 2 3
print(*[])
#
print(*[1, 2, 3])
# 1 2 3
print(*())
#
print(*(1, 2, 3))
# 1 2 3
print(*'hello')
print('h', 'e', 'l', 'l', 'o')
# h e l l o
print(*{'name': 'xm', 'age': 18})
print('name', 'age')
# name age
print(*{1, 2, 3})
# 1 2 3
2.变量名前加*的效果
# 无论传入的数据个数及类型,最终以元素保存在元组中,结合1中的例子理解
def f(*a):
print(a)
f(1, 2)
# (1,2)
def f2(*b):
print(b)
f2((1, 2), [1, 2, 3], 1)
# ((1,2),[1,2,3],1)
def f3(*c):
print(c)
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
x='hello'
f3(*x)
# ('h', 'e', 'l', 'l', 'o')
# *-* coding:utf-8 *-*
def test1(a, b, *args, **kwargs):
print(a)
print(b)
print(args)
print(kwargs)
def test2(a, b, *args, **kwargs):
print(a)
print(b)
print(args)
print(kwargs)
print('-------------')
# test1(a, b, args, kwargs) # args和kwargs是两个变量被打包进test1中的元组参数中
test1(a, b, *args, **kwargs) #
test2(1, 2, 3, 4, 5, 6, h='hello', t=1)
3.关键字参数
调用函数时,在函数括号中直接给形参赋值,不讲顺序
当函数的形参有多个缺省参数时,可通过关键字参数进行赋值
def mutable(name, action):
print(name+'->'+action)
mutable(action='change the world!', name='yl')
5-2 函数参数与局部变量
针对局部变量:
- 无论传递的参数是可变还是不可变,只要在函数内部对参数使用 赋值语句=,会在函数内部 修改 局部变量的引用,不会影响到外部变量的引用
- 如果传递的参数是 可变类型,在函数内部,使用 方法 修改了数据的内容,就会影响到外部的数据
- 可变类型变量调用+=效果等同于.extend( )方法
def demo4(num, num_list):
print('函数内部开始:')
num += num
# num = num + num # 两种方式效果相同,num的id都有变化(数字是不可变对象,只能重新创建),全局变量数字不变
num_list += num_list # 列表id不变(列表是可变对象,在原来的基础上更改,效果等同于.extend()),改变全局变量列表
# num_list = num_list + num_list # 列表id变化(列表相加产生新的列表),相当于新建一个变量
print(num)
print(num_list)
print('函数内部结束.')
gl_num4 = 9
gl_list = [1, 2, 3]
demo4(gl_num4, gl_list)
print(gl_num4, gl_list)
def mutable(num_list):
# num_list = [1, 2, 3]
num_list.extend([1, 2, 3]) # => num_list += [1, 2, 3]
print(num_list)
gl_list = [6, 7, 8]
mutable(gl_list)
print(gl_list)
# 结果
# [6, 7, 8, 1, 2, 3]
# [6, 7, 8, 1, 2, 3]
5-3 递归
函数调用自身的编程技巧
- 递归要有出口,这个出口一般是满足一定条件时,不再调用函数
-
递归要有返回值
使用递归的一般方法:
- 从一般到特殊,从大概到具体,最后的出口一般是最小值
- 假设函数(n-1)的功能可以实现
特点:
每次递归都是重新调用函数,极耗内存
网友评论