零基础学python(二)—— 字典、集合、函数、装饰器、生成器、函数文档、偏函数

1. 字典

字典的创建

最常见的创建方式:

d = {"k1": "v1", "k2": "v2"}

再向字典中添加一对键值:

d["k3"] = "v3"

字典还可以用dict()创建,这种方式中,键是不加引号的: 

d = dict(k1=v1, k2=v2, k3=v3)

dict里面也可以是列表,列表的每一个元素是元组:

d = dict([("k1", "v1"), ("k2", "v2"), ("k3", "v3")])

还可以用zip函数创建字典

d = dict(zip(["k1", "k2", "k3"], ["v1", "v2", "v3"]))

还可以用dict的fromkeys方法创建字典,第一个参数是一个可迭代序列,代表字典的key,第二个参数是初始化字典所有key的值(如果不传,则所有值为None):

d = dict.fromkeys("Fish", 250)

使用pop方法删除指定而key:

d.pop("s")

pop一个不存在的键会抛出异常:KeyError

可以用popitem()随机删除一个键,Python 3.7之后,则是删除最后一个加入字典的键

使用del可以删除字典的键或者整个字典:

del d["I"]
del d

如果只是想清空字典的内容,使用clear方法:

d.clear()

如果想修改字典的多个键值对,可使用update方法:

d.update({"i": 250, "s": 250})
d.update(h=70, F=666)

如果需要设置某个键的值,如果存在该键,则不更新,如果没有,则设置为该值,此时需要使用setdefault()方法:

d.setdefault('c', 'code')

如果需要遍历字典的每一个key-value或者每一个key或者每一个value,则可以使用items()、keys()、values()方法,这三个方法返回的是字典的视图对象。使用list可以将key和value的视图对象转换为列表:

list(d.keys()) # 相当于list(d)
list(d.values())

字典也可以使用推导式,使用推导式将key和value反转过来:

b = {v:k for k,v in d.items()}
c = {v:k for k,v in d.items() if v > 100}
d = {x: ord(x) for x in "FishC"}

2. 集合

集合的基础创建方法、特征、使用方法等不在此介绍,这里介绍一些进阶的操作。

检测两个集合之间是否毫不相关,使用isdisjoint方法:

s = set("FishC")
s.isdisjoint(set("Python"))

 结果是False,因为他们有共同的h。disjoint方法不必传入集合,只要传入可迭代对象即可:

s = set("FishC")
s.isdisjoint("Java")

检测一个集合是否为另一个集合/可迭代对象的子集:

s.issubset("FishC.com.cn")

检测一个集合是否为另一个集合/可迭代对象的超集(父集): 

s.issuperset("Fish")

计算并集、交集、差集、对称差集: 

s.union({1, 2, 3}) # 并集
s.intersection("Fish") # 交集
s.difference("Fish") # 差集
s.symmetric_difference("Python") # 对称差集,相当于并集 - 交集

除了使用集合的方法确定子集和超集,以及计算并集、交集、差集、对称差集,也可以使用运算符确定子集和超集,以及计算并集、交集、差集、对称差集:

s >= set("Fish") # 是否是超集
s < set("Fish.com.cn") # 是否是子集
s | set("Python") # 并集
s & set("Python") # 交集
s - set("Python") # 差集
s ^ set("Python") # 对称差集

值得注意的是,使用方法时,方法的参数只要是可迭代对象即可,但是使用运算符,运算符两边都要为集合。

Python中的不可变集合:frozenset

s = set("FishC")
t = frozenset("FishC")

尝试使用update方法更新集合的元素:

s.update([1, 1], "23") # 不报错
t.update([1, 1], "23") # 报错

 update方法传入的是可迭代对象,是更新一系列的值,如果只想添加单个元素,可以使用add方法:

s.add("45")

注意,上述代码会将45作为一个整体字符串插入集合,而不是插入4和5。

删除集合的元素:remove和discard:

s.remove("xxx")
s.discard("xxx")

两者的区别在于,如果元素不存在,remove方法会报错,discard方法不会报错

随机从集合弹出一个元素:pop

s.pop()

注意只有可哈希的对象才能作为字典的key和集合的元素,比如字符串,数字,元祖,但是列表不可以作为字典的key和集合的元素,列表是可变数据类型,是不可哈希的。所以嵌套集合一般是不可以的,因为集合也是可变的数据类型。如果非要嵌套集合,可以使用frozenset,即集合里嵌套frozenset。

3. 函数的参数

python函数的传参分为位置参数和关键字参数两种,比如如下函数定义:

使用位置参数调用:

使用关键字参数调用:

如果要混合使用关键字参数和位置参数,位置参数必须在关键字参数之前

另外默认参数必须放在非默认参数的后面(函数定义时需要满足此规则)。

函数定义时,有时会看到参数为/,这表示/左边的参数必须是位置参数,不能是关键字参数:

/右边的参数可以是位置参数,也可以是关键字参数。

*则相反,*右边的参数必须是关键字参数,*左边的参数可以是位置参数,也可以是关键字参数:

如果可变参数(一般是*args表示)后面还有参数,则传参时必须用关键字参数,否则后面传的参数也会被认为是可变参数:

​​​​​​​ 

4. 函数的嵌套与闭包

函数如果嵌套,内层函数是无法直接修改外层函数的变量,这有点像一个函数内无法直接修改全局变量一样:

def funA():
    x = 520
    def funB():
        x = 880
        print("In funB, x = ", x)
    funB()
    print("In funA, x = ", x)

调用funA,运行结果如下:

类似于引入global变量,这里引入nonlocal变量,就可以在funB中修改funA的变量了:

def funA():
    x = 520
    def funB():
        nonlocal x
        x = 880
        print("In funB, x = ", x)
    funB()
    print("In funA, x = ", x)

对于函数的嵌套,如果内层函数(一段代码处理逻辑)使用到了外层函数的变量(相当于外层函数的局部变量),然后外层函数的返回值恰好是内层函数本身,那么调用外层函数就可以获得内层函数的引用,并且里面相当于间接的永久保存了外层函数的局部变量,这就是闭包。

如上图代码中,funA调用完毕之后,其中的局部变量x的生命周期理论上已经结束,但是再次调用funny(),却能够打印x的值。这就是闭包的好处:外层函数的作用域可以通过内层函数间接永久保存下来。

如果内层函数使用nonlocal语句修改外层函数的变量,那就更有意思了(坐标移动):

5. 装饰器

装饰器的原理就是将函数作为参数传递给另一个参数,举个例子:

import time
def time_master(func):
    print("开始运行程序")
    start = time.time()
    func()
    stop = time.time()
    print("结束程序运行")
    print(f"一共耗费了{(start - stop):.2f}秒")
    
def myfun()
    time.sleep(2)
    print("Hello")

time_master(myfunc())

如果每次想要知道函数的运行时间,都要显示地调用time_master,显然比较麻烦,如果能放函数执行时自动统计运行时间,就很方便了,此时就需要用到装饰器:

import time
def time_master(func):
    def call_func():
        print("开始运行程序")
        start = time.time()
        func()
        stop = time.time()
        print("结束程序运行")
        print(f"一共耗费了{(start - stop):.2f}秒")
    return call_func()
    
@time_master
def myfun()
    time.sleep(2)
    print("Hello")

myfunc()

装饰器的原理就是将被装饰的函数作为参数传进装饰器函数中,相当于:

myfunc = timemaster(myfunc)

多个装饰器可以装饰同一个函数,执行顺序是从下至上:

运行结果是65。

给装饰器传参数,需要再加一层嵌套,第一层函数传递参数,第二层函数传递函数:

6. 生成器

定义生成器很简单,在函数中使用yield代替return语句就可以了:

def counter():
    i = 0
    while i <= 5:
        yield i
        i += 1

调用函数不会得到i的值,会得到一个生成器,如果要获取生成器的值,使用for语句:

for i in counter():
    print(i)

 生成器可以看成一个特殊的迭代器,每调用一次产生一个数据,并暂停,可以使用next函数获取其值:

使用生成器产生斐波那契数列:

def fbn():
    x = 0
    y = 1
    While True:
        yield x
        x, y = y, x + y

f = fnb()
next(f)
next(f)
next(f)
next(f)
next(f)

元祖的推导式是一个生成器,所以元祖的推导式也叫生成器表达式:

t = (i ** 2 for i in range(10))

 

7. 函数文档

使用help函数查看函数的功能帮助:

help函数的输出其实是函数的文档,用""" """"包裹:

也可以使用函数名.__doc__查看函数文档(带有\n换行符,建议用print查看):

通过函数名.__annotations__查看参数类型注释:

 8. 偏函数和@wraps装饰器

偏函数就是让函数的多个参数分开传递,每次传递一个参数就可以得到一个新的函数

 使用装饰器可以丰富函数的功能,但是会有副作用,改变函数的__name__属性:

查看myfunc.__name__,结果竟然是call_func,本质是因为使用了装饰器之后,等同于myfunc = time_master(myfunc),所以是call_func。为了解决这个问题,需要使用@wraps装饰器:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值