Python进阶(三)----函数名,作用域,名称空间,f-string,可迭代对象,迭代器
一丶关键字:global,nonlocal
global 声明全局变量:
1. 可以在局部作用域声明一个全局变量
2. 可以修改全局变量
# 解释器认为: 如果你在局部作用域对一个变量进行修改,局部作用域已经定义了好这个变量
# 异常:
# UnboundLocalError: local variable 'count' referenced before assignment
####相当于局部声明的变量,剪切到全局
count=0
print(locals()) # count:0
def func():
global count #定义成全局变量
count+=1 # 此处由于 要修改count的值, 现在函数内没有定义count的变量, 如果想要修改全局变量count 需要global声明一下
print(locals()) # 查看当前(现在就是局部空间)空间的变量和值, 没count变量
func()
print(locals()) # 查看当前(现在就是全局空间)空间的变量和值 ,count:1
nonlocal 局部作用域如果想对父级作用域的变量(python3x新加的功能):
1.可以对父级别作用域的变量进行修改,并且在当前作用域创建
2.不能操作全局作用域
####复制 , 共用一块内存地址
def func():
count=0 #局部变量
def inner():
nonlocal count #inner函数内 修改 count 变量, 需要nonlocal声明
count+=3
print(count)
print(id(count)) #id() 查看这个inene函数中count变的内存地址 -->1952940208(内存地址)
print(locals()) #查看当前作用域中的变量和值 {'count': 3}
inner()
print(locals()) #{'inner':<function func.<locals>.inner at 0x00000140E1ABBD90>, 'count': 3}
print(id(count)) #id() 查看这个func函数中count变的内存地址 1952940208(内存地址)
func()
二丶形参中---默认参数的坑
###当你的默认参数的数据类型是可变的数据类型时 千万注意,使用的是同一个可变类型数据的内存地址 so看代码?
#默认参数是字典
def func(a,b={}):
b[a+1]=a
return b
print(func(1,))
print(func(2,))
print(func(3,))
print(func(5,))
#默认参数是列表类型
def func(a,b=[]):
b.append(a)
return b
print(func(1,))
print(func(1))
print(func(1))
func(1,2)
###例题
#例1
def extendList(val,list=[]): #默认创建一个列表
list.append(val)
return list
list1 = extendList(10) # [10 ,'a'] # 往默认列表添加元素
list2 = extendList(123,[]) # [123,] # list被一个新的列表替换,新列表是一个空列表,so 只添加123
list3 = extendList('a') #[10,'a'] # 继续往默认的列表添加内容
print('list1=%s'%list1) ##上面的操作完成之后, list1 和list3 使用的是同一个内存地址, 共享一个列表
print('list2=%s'%list2) ##上面的操作完成之后, list2 使用的是新的列表,so .
print('list3=%s'%list3) ##上面的操作完成之后, list1 和list3 使用的是同一个内存地址, 共享一个列表
# 例2 这道题和上面的题一样,但是 ,别忘记了print() 打印当前的操作完成之后的结果.
def extendList(val,list=[]):
list.append(val)
return list
print('list1=%s'% extendList(10)) # [10] # 往默认列表添加元素, 就直接打印 so .现在这个默认列表就只有 10
print('list2=%s'% extendList(123,[])) #[123] #往新的列表添加元素, so 和默认列表毫无关系
print('list3=%s'% extendList('a')) #[10,'a'] #往默认列表添加元素,之前已经添加了10 ,so 现在追加了'a'元素,so,现在默认列表就有[10,'a']
三丶函数名的应用
函数名:就是一个特殊的变量 , 别称:第一类对象
函数名指向的是一个函数的内存地址,函数名加上()就能够执行函数
###废话不多说,上代码?
## 1. 函数名可以当做一个变量 ,具有变量的赋值功能
def func():
print('in func ')
print(func) #打印函数func的内存地址 自己打
a=func # 将函数的内存地址给了a
c=a #相当于c变量拿到 a 或者 func的函数内存地址
c() #相当于 c()=func(), 执行函数func()
## 2. 函数名容器类类型的元素
def func1():
print('in func1')
def func2():
print('in func2')
def func3():
print('in func3')
li=[func1,func2,func3]
for el in li: # 循环每个元素, el就是函数的内存地址
el() # 执行每个函数
#拓展?:你要是有2000个函数,你总不能写2k个"函数明()". 用for循环执行2k个函数 so easy
# 3. 函数名 可以作为函数的实参
def func(argv):
argv() #接收传来的函数名+() --->执行这个函数
print('in func')
def inner():
print('in inner')
func(inner) #把其他的函数的名作为参数传给func函数的argv
# 4.函数名可以作为函数的返回值
def func():
print('in func')
return inner #返回 其他函数的函数名,
def inner():
print('in inner')
func()() # 相当于func()=inner --->inner() 执行inner函数
#5. 函数名+() 可以执行函数
def func():
print('被执行了')
func()
四丶python新特性字符串输出:f-string
新特性:f-strings 是python3.6 格式化输出新的写法,效率高,更简化,好用
####看代码?
#### 用法格式
name='宇宙盖世无敌小keNai,?嘻嘻'
s=f'我叫 {name}' # {变量,函数名(),表达式} ,除了特殊的字符如: ;分号 :冒号 ,逗号(变量前) !
#用法①: 直接操作字符串
s1='李四'
s2=f'哒哒哒哒哒{s1.upper()}'
print(s2)
#用法②: 放置列表元素
li=['太白',18,176]
s1=f'{li[0]},{li[1]} ,{li[2]}'
print(s1)
#用法③: 可以结合函数
def func(a,b):
return a+b
s=f'{func(1,2)}'
print(s)
五丶可迭代对象
含义:
可以重复的迭代的实实在在的东西
专业角度:
内部__ iter __ 方法的对象,就是可迭代对象
#常见的可迭代对象: list , tuple ,dict ,str
# 内置函数 ,查看所有方法,属性 ,得到的所有方法都以字符串的形式放在列表中
print(dir(str))
#验证是不是 可迭代对象
s1 = '不打广告'
i = 100
print('__iter__' in dir(i)) # False
print('__iter__' in dir(s1)) # True
#常用的方法验证 xx 是不是一个可迭代对象
print('__iter__' in dir(str))
#可迭代对象的优缺点
#优点:
1.直观
2.操作方法多
#缺点:
1. 消耗内存较大
2. 不能迭代取值(索引)
六丶迭代器
含义:
是一个可以迭代取值的工具
专业角度:
内部含有 __iter__方法 并且含有 __next__方法 就是迭代器
#迭代器是看不到内存地址
####可迭代对象转化成迭代器:
#可迭代对象转化成迭代器 , 内置函数iter() 和 __iter__() 两种方式
li=[1,2,3]
obj=iter(li) #把迭代对象转换成迭代器
objg=li.__iter__() #把迭代对象转换成迭代器
print(type(obj),type(objg)) #<class 'list_iterator'>
print(obj) # <list_iterator object at 0x000002075D219518> 迭代器
####迭代器可以迭代取值, 利用内置函数next()进行取值 . 取完值,值就消失了
li=[1,2,3]
obj=iter(li)
print(next(obj)) # 迭代一次,取值一次,值取完就没了.销毁
print(next(obj))
print(next(obj))
print(next(obj)) # 当迭代器中没有值可取了 ,会把报错 StopIteration
####利用while 模拟 for 循环内部循环可迭代对象的机制
#1.先将可迭代对象转换成迭代器
#2.利用next对迭代对象进行取值
#3.利用异常处理try一下 防止报错~~
#方法1
try:
li = [1, 2, 3, 4, 5]
li_itera = iter(li)
while 1:
print(li_itera.__next__())
except StopIteration:
pass
#方法2
li = [1, 2, 3, 4, 5]
li_itera = iter(li)
while 1:
try:
print(next(li_itera))
except:
break
#迭代器是一条路走到底(记录位置)
li=[1,2,3,4,]
obj=iter(li)
for index in range(2): # 1 2 ,取完值,值就销毁
print(obj.__next__())
for index in range(1): # 3
print(obj.__next__())
七丶可迭代对象与迭代器的对比
####可迭代对象:
可迭代对象是一个操作比较灵活(比如列表,字典的增删改查,字符串的常用操作方法等),直观,效率相对高,
但是占用内存,而且不能直接通过循环迭代取值的这么一个数据集。
####迭代器:
可以记录取值位置,以直接通过循环+next方法取,迭代器是一个非常节省内存,满足惰性机制,
但是相对效率低,操作方法比较单一的数据集。(可参考为什么python把文件句柄设置成迭代器)