python-基础
day 12
函数
实参与形参
-
实参
分为三种:
位置参数,关键字参数,混合参数
混合参数的顺序: 位置参数 > 关键字参数 -
形参
分为三种:
位置参数,默认值参数,动态参数(动态接收位置参数 *args 为元组,动态接收关键字参数**kwargs 为字典)
形参顺序:位置参数 > *args > 默认值参数 > **kwargs
def lunch(rice,*args,fruit="葡萄",vatble="白菜",**kwargs):
print("今天午餐吃",rice,args,fruit,vatble,kwargs)
lunch("东北大米","贵州苞米","崇明鲜米",fruit="芒果",vatble="鸡毛菜",meat="牛肉",big_meat="烤全羊")
输出:
今天午餐吃 东北大米 ('贵州苞米', '崇明鲜米') 芒果 鸡毛菜 {'meat': '牛肉', 'big_meat': '烤全羊'}
聚合与打散
- 形参:* 聚合成一个元组,**聚合成一个字典
- 实参:* 打散可迭代对象(字符串,元组,列表,字典等)
def func1(*args): # *表示聚合,所有的位置参数, 聚合成元组
print(args)
st="abcd"
lst = ["马虎疼", "大耳朵", "小花生", "毛尖妹妹"]
dic = {"name":"半泽", "age":"18"}
tup = ("张哥哥","林妹妹")
func1(*st)
func1(*lst) # 实参, 打散, 迭代产生的
func1(*dic) #字典用单个*打散只传入key值
func1(*tup)
func1(*range(4))
#
def func2(**kwargs): # **聚合成字典
print(kwargs)
func2(**dic) # 把字典打散. 以key=value形式进行传参
输出:
('a', 'b', 'c', 'd')
('马虎疼', '大耳朵', '小花生', '毛尖妹妹')
('name', 'age')
('张哥哥', '林妹妹')
(0, 1, 2, 3)
{'name': '半泽', 'age': '18'}
命名空间
在python解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表⽰这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关⼼的. 也就是说⼀开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调⽤和访问的时候, 解释器才会根据函数内部声明的变量来进⾏开辟变量的内部空间. 随着函数执⾏完毕, 这些函数内部变量占⽤的空间也会随着函数执⾏完毕⽽被清空。
命名空间:存放名字和值的关系的空间。
命名空间分类:
- 全局命名空间--> 我们直接在py⽂件中, 函数外声明的变量都属于全局命名空间
- 局部命名空间--> 在函数中声明的变量会放在局部命名空间
- 内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
执行一个程序时,命名空间的加载顺序: 内置命名空间 全局命名空间 局部命名空间
作⽤域: 作⽤域就是作⽤范围, 按照⽣效范围来看分为 全局作⽤域和局部作⽤域。
作⽤域命名空间:
- 全局作⽤域: 全局命名空间 + 内置命名空间
- 局部作⽤域: 局部命名空间
我们可以通过globals()函数来查看全局作⽤域中的内容, 也可以通过locals()来查看局部作⽤域中的变量和函数信息。
locals()放在哪里,就显示哪里的命名空间变量,放在外层就显示全局命名空间。
globals()放在哪里都是显示全局作用域的命名空间变量。
name="name1"
def A():
name="name2"
def func():
pass
print(locals())
a=A()
print(globals())
输出:
{'name': 'name2', 'func': <function A.<locals>.func at 0x0000021262C5C430>}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000021260916CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (buoader object at 0ilt-in)>, '__file__': 'd:\\Desktop\\python_script\\homewk.py', '__cached__': None, 'os': <module 'os' from 'D:\\办公__': 'd:\\Desktop
\\python\\lib\\os.py'>, 'I': re.IGNORECASE, 'urlopen': <function urlopen at 0x0000021260F01F70>, 'name': 'name1', 'ANORECASE, 'urlope': <function A at 0x000002126095DF70>, 'a': None}
global与nonlocal
global <变量a> 表示在局部作用域中,调用全局作用域(包括全局命名空间与内置命名空间)的a变量,还能实现在局部对全局的变量进行修改。
nonlocal <变量a> 表示在局部作⽤域中, 调⽤⽗级命名空间中的变量,还能对父级命名空间的变量进行修改。
global:
name="name1"
def A():
name="name2"
def func():
global name # global 在内层加载全局变量name
print("修改前内层func name:",name)
name = "name3" # 在内层修改全局变量name
print("修改后内层func name:",name)
func()
print("父级name:",name)
a=A()
print("全局name:",name) # 查看全局变量已改变
输出:
修改前内层func name: name1
修改后内层func name: name3
父级name: name2
全局name: name3
nonlocal:
name="name1"
def A():
name="name2"
def func():
nonlocal name # nonlocal 在内层加载父级局部变量name
print("修改前内层func name:",name)
name = "name3" # 在内层修改父级局部变量name
print("修改后内层func name:",name)
func()
print("父级name:",name) #父级局部变量name已改变
a=A()
print("全局name:",name)
输出:
修改前内层func name: name2
修改后内层func name: name3
父级name: name3
全局name: name1
闭包
闭包就是在内层函数中调用外层函数(非全局)的变量。
随着函数执⾏完毕, 函数内部变量占⽤的空间也会随着函数执⾏完毕⽽被清空,而如果有闭包函数,则会让那个在内部函数中被调用的外部函数变量常驻内存。
闭包可以让一个局部变量常驻内存,例如:
def A():
name="san_zhang"
def func():
print(name)
print(func.__closure__) #查看某个函数是否为闭包
return func
a1=A() #一般在A()调用完成后,将返回值传递给a1,A()对应的空间就会删除,但当有闭包情况时,被内层调用的那个外层变量name仍会保留
a2=a1()
输出:
(<cell at 0x000001B89BDD03A0: str object at 0x000001B89BE1DE30>,)
san_zhang
使⽤__closure__来检测函数是否是闭包. 使⽤函数名.__closure__返回cell就是闭包,返回None就不是闭包。
迭代器
可迭代对象(Iterable):有__iter__方法的对象都为可迭代对象。例如str, list, tuple, dict, set都能使用for循环,int没有__iter__方法,所以不是可迭代对象,使用for循环也会报错。
dir()返回当前范围能使用的变量、方法;
dir(a)查看对象a的属性与方法,返回为列表。
例如:
lst = [1,2,3]
print(dir(lst))
输出: #有__iter__
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
迭代器(Iterator): 有__iter__ 与 __next__两种方法的对象为迭代器。
如何生成一个迭代器?以下it就是一个迭代器。迭代器通过__next__来逐个取值。
it=可迭代对象.__iter__()
it.__next__() #返回第一个值
it.__next__() #返回第二个值
lst = [1,2,3] # list
it=lst.__iter__()
print(it.__next__())
print(it.__next__())
print(it.__next__())
print(it.__next__())
输出: 当使用__next__取不到值时会报错StopIteration。
1
2
3
Traceback (most recent call last):
File "d:\Desktop\python_script\test.py", line 710, in <module>
print(it.__next__())
StopIteration
迭代器也能使用其他方法获取值,例如for循环与list()
lst1 = [1,2,3]
lst2 = [4,5,6]
it1=lst1.__iter__()
it2=lst2.__iter__()
print(list(it1))
for i in it2:
print(i)
判断一个对象是可迭代对象或迭代器
from collections import Iterable
from collections import Iterator
lst = [1,2,3] # list
it=lst.__iter__()
print(isinstance(lst, Iterable))
print(isinstance(it, Iterator))
输出:
True
True
使⽤while循环+迭代器来模拟for循环(必须要掌握)
lst = [1,2,3] # list
for i in lst:
print(i)
------------相当于----------------
lst = [1,2,3]
it=lst.__iter__()
while 1:
try:
ret=it.__next__()
print(ret)
except StopIteration:
break
总结:
Iterable: 可迭代对象. 内部包含__iter__()函数。
Iterator: 迭代器. 内部包含__iter__() 同时包含__next__()。
迭代器的特点:
- 节省内存:用一个值取一个值,而不是一次性全取完放内存中。
- 惰性机制:不调用__next__,不返回。
- 不能反复, 只能向下执⾏。
生成器
生成器(generator)的本质也是迭代器,我们可以通过以下三种方法获取生成器。
- 生成器函数
- 通过各种推导式来实现⽣成器
- 通过数据的转换也可以获取⽣成器
生成器函数
将函数的return换成yield,函数调用的对象就是一个生成器。与return不同的是,yield可以分段执行,return之后就不会再执行了。生成器本质是迭代器,也使用__next__逐个取值。
def eat():
yield("草莓")
yield("樱桃")
f=eat()
print(f.__next__())
print(f.__next__())
输出:
草莓
樱桃
生成器也可使用send方法取值。与__next__的区别:
- send和next()都是让⽣成器向下走⼀次
- send("value")可以将value传到上一个yield的位置,取第一个值时不能用send(),不能给最后一个yield发送值。
def eat():
a=yield("草莓")
print(a)
b=yield("樱桃")
print(b)
c=yield("芒果")
it=eat()
print(dir(f))
print(it.__next__())
print(it.send("pear"))
print(it.send("apple"))
输出:
草莓
pear
樱桃
apple
芒果
⽣成器可以使⽤for循环来循环获取内部的元素
def eat():
print(111)
yield("草莓")
print(222)
yield("樱桃")
print(333)
yield("苹果")
it=eat()
for i in it:
print(i,"end")
输出:
111
草莓 end
222
樱桃 end
333
苹果 end
列表推导式, ⽣成器表达式以及其他推导式
列表推导式常用写法:
[ 结果 for 变量 in 可迭代对象]
[ 结果 for 变量 in 可迭代对象 if条件]
所有的结果组成一个列表返回
例如:
for i in range(5):
print(i)
改为列表推导式为:
lst=[i for i in range(5)]
print(lst)
输出:
0
1
2
3
4
[0, 1, 2, 3, 4]
带条件的列表推导式:
返回range(10)中偶数的平方
lst=[i*i for i in range(10) if i % 2 == 0]
print(lst)
输出:
[0, 4, 16, 36, 64]
生成器表达式就是把列表推导式的[]换成(),最终的结果是一个生成器,需要逐个取值。
#将names列表中带有两个"e"的名字删选出来
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven',
'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
#常规for循环
name_lst=[]
for lst in names:
for name in lst:
if name.count("e") >= 2:
name_lst.append(name)
print(name_lst)
#列表推导式
name_lst2=[name for lst in names for name in lst if name.count("e") >= 2]
print(name_lst2)
#生成器表达式
name_lst3=(name for lst in names for name in lst if name.count("e") >= 2)
for i in name_lst3: #用for取值
print(i)
列表推导式与器表达式与区别:
- 占用内存不同,列表推导式是将值一次性全部取出放到内存,成器表达式是一次取一个。
- 获取的结果不同,列表推导式的的结果是一个列表,成器表达式的结果是一个生成器。
字典推导式
与列表推导式类似,将[]换成{},但获取的结果要是key:value类型,最终返回一个字典。
a=(('cat'), ('dog'),("mouse"))
b=(('咪咪'), ('来福'),("吱吱"))
dic={a[i]:b[i] for i in range(len(a))}
print(dic)
输出:
{'cat': '咪咪', 'dog': '来福', 'mouse': '吱吱'}
集合推导式
与列表推导式类似,将[]换成{},有去重功能,但获取的结果不能是key:value类型(因为这个类型就变成了字典推导式了),最终返回一个集合。
lst=[9,5,4,4,6,5,3]
ret={i for i in lst}
print(ret)
输出:
{3, 4, 5, 6, 9}
总结:
推导式有: 列表推导式, 字典推导式, 集合推导式, 没有元组推导式
⽣成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)
⽣成器表达式可以直接获取到⽣成器对象. ⽣成器对象可以直接进⾏for循环,⽣成器具有惰性机制。
python 中的模板字符串
f'{变量}' 引用变量对应的值
name='hsahha'
print(f'my name is {name}')
# my name is hsahha
函数类型注解
以下函数有两个形参a,b,都应是int类型,返回的是二者只和也是int类型。
最好加上类型注解告诉适用者应该传入和返回的参数类型
def sum (a,b):
return a+b
类型注解方式一:
def sum (a,b):
"""
:param a: int
:param b: int
:return: int
"""
return a+b
类型注解方式二:
# a:b 表示 a形参应是整数类型; ->int 表示返回值是整数类型
def sum (a:int,b:int)->int:
return a+b