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解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表⽰这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关⼼的. 也就是说⼀开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调⽤和访问的时候, 解释器才会根据函数内部声明的变量来进⾏开辟变量的内部空间. 随着函数执⾏完毕, 这些函数内部变量占⽤的空间也会随着函数执⾏完毕⽽被清空。

命名空间:存放名字和值的关系的空间。

命名空间分类:

  1. 全局命名空间--> 我们直接在py⽂件中, 函数外声明的变量都属于全局命名空间
  2. 局部命名空间--> 在函数中声明的变量会放在局部命名空间
  3. 内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间

执行一个程序时,命名空间的加载顺序: 内置命名空间 全局命名空间 局部命名空间

作⽤域: 作⽤域就是作⽤范围, 按照⽣效范围来看分为 全局作⽤域和局部作⽤域。
作⽤域命名空间:

  1. 全局作⽤域: 全局命名空间 + 内置命名空间
  2. 局部作⽤域: 局部命名空间

我们可以通过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__()。

迭代器的特点:

  1. 节省内存:用一个值取一个值,而不是一次性全取完放内存中。
  2. 惰性机制:不调用__next__,不返回。
  3. 不能反复, 只能向下执⾏。

生成器

生成器(generator)的本质也是迭代器,我们可以通过以下三种方法获取生成器。

  1. 生成器函数
  2. 通过各种推导式来实现⽣成器
  3. 通过数据的转换也可以获取⽣成器

生成器函数

将函数的return换成yield,函数调用的对象就是一个生成器。与return不同的是,yield可以分段执行,return之后就不会再执行了。生成器本质是迭代器,也使用__next__逐个取值。

def eat():
    yield("草莓")
	yield("樱桃")
f=eat()
print(f.__next__())
print(f.__next__())
输出:
草莓
樱桃

生成器也可使用send方法取值。与__next__的区别:

  1. send和next()都是让⽣成器向下走⼀次
  2. 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)

列表推导式与器表达式与区别:

  1. 占用内存不同,列表推导式是将值一次性全部取出放到内存,成器表达式是一次取一个。
  2. 获取的结果不同,列表推导式的的结果是一个列表,成器表达式的结果是一个生成器。

字典推导式
与列表推导式类似,将[]换成{},但获取的结果要是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
posted @ 2021-10-05 15:08  huandada  阅读(48)  评论(0编辑  收藏  举报