*args
和 **kwargs
对于刚开始学习Python的读者,一定在编写代码的时候,遇到过这个问题。他们到底是怎么回事呢?且听我一一道来。
首先你需要知道的是:其实我们并不是一定要写成*args
和
**kwargs
,只有前面的*
才是必须有的。因此,我们也可以写成*var
和**var
而写成*args
和
**kwargs
只是一个通俗的命名约定而已。接下来给大家首先介绍*args
。
*args
的用法*args
和**kwargs
主要用于函数定义。 你可以将不定数量的参数传递给一个函数。在这里的不定的意思是:预先并不知道,
函数使用者会传递多少个参数给你, 所以在这个场景 下使用这两个关键字。 *args
是用来发送一个非键值对的可变数量的参数列表给一个函数。这里有个例子帮我们理解这个概念:
def test_var_args(f_arg, *argv):
print("first normal arg:", f_arg)
for arg in argv:
print("another arg through *argv:", arg)
test_var_args('yasoob', 'python', 'eggs', 'test')
具体的执行结果如下:
通过这个案例,我希望这个小的知识点已经讲清楚了。接下来接着介绍**kwargs
。
**kwargs
的用法**kwargs
允许你将不定长度的键值对, 作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数,
我们应该使用**kwargs
。我们同样通过一个案例来进行说明:
def greet_me(**kwargs):
for key, value in kwargs.items():
print("{0} == {1}".format(key, value))
greet_me(name="yasoob")
现在可以看出我们怎样在一个函数里, 处理了一个键值对参数了,具体执行结果如下:
这就是**kwargs
的基础, 而且你可以看出它有多么管用。 接下来让我们谈谈,我们怎样使用*args 和
**kwargs来调用一个参数为列表或者字典的函数。
*args
和**kwargs
来调用函数那现在我们将看到怎样使用 args和 *kwargs 来调用一个函数。 假设,你有这样一个小函数:
def test_args_kwargs(arg1, arg2, arg3):
print("arg1:", arg1)
print("arg2:", arg2)
print("arg3:", arg3)
args = ("two", 3, 5)
test_args_kwargs(*args)
具体执行结果如下:
我们现在使用 **kwargs
:将其进行实现:
def test_args_kwargs(arg1, arg2, arg3):
print("arg1:", arg1)
print("arg2:", arg2)
print("arg3:", arg3)
kwargs = {"arg3": 3, "arg2": "two", "arg1": 5}
test_args_kwargs(**kwargs)
具体执行的结果如下:
那么如果你想在函数里同时使用所有这三种参数, 顺序是这样的:
some_func(fargs, *args, **kwargs)
这个还是需要特定的需求具体而定;最常见的用例是在写函数装饰器的时候。此外它也可以用来做猴子补丁
。猴子补丁的意思是在程序运行时(runtime)修改某些代码。
打个比方,你有一个类,里面有个叫get_info
的函数会调用一个API
并返回相应的数据。如果我们想测试它,可以把API调用
替换成一些测试数据。
利用好调试,能大大提高你捕捉代码Bug的。大部分新人忽略了Python debugger(pdb)的重要性。
在这个章节我只会告诉你一些重要的命令,你可以从官方文档中学习到更多。
我们可以在命令行使用Python debugger
运行一个脚本, 举个例子:
python -m pdb my_script.py
这会触发debugger
在脚本第一行指令处停止执行。这在脚本很短时会很有帮助。你可以通过(Pdb)模式接着查看变量信息,并且逐行调试。
同时,你也可以在脚本内部设置断点,这样就可以在某些特定点查看变量信息和各种执行时信息了。这里将使用pdb.set_trace()方法来实现。举个例子:
import pdb
def make_bread():
pdb.set_trace()
return "I don't have time"
print(make_bread())
试下保存上面的脚本后运行之。你会在运行时马上进入debugger模式。具体效果如下:
现在是时候了解下debugger模式下的一些命令了。
以下是一些常用的命令:
c
: 继续执行w
: 显示当前正在执行的代码行的上下文信息a
: 打印当前函数的参数列表s
: 执行当前代码行,并停在第一个能停的地方(相当于单步进入)n
: 继续执行到当前函数的下一行,或者当前行直接返回(单步跳过)
单步跳过(next)和单步进入(step)的区别在于: **单步进入会进入当前行调用的函数内部并停在里面,
而单步跳过会(几乎)全速执行完当前行调用的函数,并停在当前函数的下一行。**
pdb真的是一个很方便的功能,上面仅列举少量用法,更多的命令强烈推荐你去看官方文档。
首先我们要理解迭代器(iterators)。根据维基百科,迭代器是一个让程序员可以遍历一个容器(特别是列表)的对象。然而,一个迭代器在遍历并读取一个容器的数据元素时,并不会执行一个迭代。你可能有点晕了,那我们来个慢动作。换句话说这里有三个部分:
可迭代对象(Iterable) 迭代器(Iterator) 迭代(Iteration)
上面这些部分互相联系。我们会先各个击破来讨论他们,然后再讨论生成器(generators).。
Python中任意的对象,只要它定义了可以返回一个迭代器的__iter__方法
,或者定义了可以支持下标索引的__getitem__方法
(这些双下划线方法会在其他章节中全面解释),那么它就是一个可迭代对象。简单说,可迭代对象就是能提供迭代器的任意对象。那迭代器又是什么呢?
__next__方法
,它就是一个迭代器。就这么简单。现在我们来理解迭代(iteration)。undefined用简单的话讲,它就是从某个地方(比如一个列表)取出一个元素的过程。当我们使用一个循环来遍历某个东西时,这个过程本身就叫迭代。现在既然我们有了这些术语的基本理解,那我们开始理解生成器吧。def generator_function():
for i in range(10):
yield i
for item in generator_function():
print(item)
具体执行结果如下:
其实,这个案例并不是非常实用。生成器最佳应用场景是:==你不想同一时间将所有计算出来的大量结果集分配到内存当中,特别是结果集里还包含循环。==
这里需要注意的是:许多Python 2里的标准库函数都会返回列表,而Python
3都修改成了返回生成器,因为生成器占用更少的资源。下面是一个计算斐波那契数列的生成器:
def fibon(n):
a = b = 1
for i in range(n):
yield a
a, b = b, a + b
for x in fibon(1000000):
print(x)
具体执行结果如下:
用这种方式,我们可以不用担心它会使用大量资源。然而,之前如果我们这样来实现的话:
def fibon(n):
a = b = 1
result = []
for i in range(n):
result.append(a)
a, b = b, a + b
return result
这也许会在计算很大的输入参数时,用尽所有的资源。我们已经讨论过生成器使用一次迭代,但我们并没有测试过。在测试前你需要再知道一个Python内置函数:next()。它允许我们获取一个序列的下一个元素。那我们来验证下我们的理解:
def generator_function():
for i in range(3):
yield i
gen = generator_function()
print(next(gen))
print(next(gen))
print(next(gen))
具体执行结果如下:
我们可以看到,在yield掉所有的值后,next()
触发了一个StopIteration
的异常。基本上这个异常告诉我们,所有的值都已经被yield
完了。你也许会奇怪,为什么我们在使用for循环时没有这个异常呢?啊哈,答案很简单。for循环会自动捕捉到这个异常并停止调用next()
。
Map,Filter 和 Reduce 三个函数能为函数式编程提供便利。我们会通过实例一个一个讨论并理解它们。
Map会将一个函数映射到一个输入列表的所有元素上。这是它的规范:
map(function_to_apply, list_of_inputs)
大多数时候,我们要把列表中所有元素一个个地传递给一个函数,并收集输出。比方说:
items = [1, 2, 3, 4, 5]
squared = []
for i in items:
squared.append(i**2)
print(squared)
具体执行结果如下:
Map可以让我们用一种简单而漂亮得多的方式来实现。就是这样:
items = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, items))
print(squared)
具体执行结果如下:
大多数时候,我们使用匿名函数(lambdas)来配合map, 所以我在上面也是这么做的。 不仅用于一列表的输入, 我们甚至可以用于一列表的函数!
def multiply(x):
return (x*x)
def add(x):
return (x+x)
funcs = [multiply, add]
for i in range(5):
value = map(lambda x: x(i), funcs)
print(list(value))
具体执行结果如下:
顾名思义,filter过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表,符合要求即函数映射到该元素时返回值为True. 这里是一个简短的例子:
number_list = range(-5, 5)
less_than_zero = filter(lambda x: x < 0, number_list)
print(list(less_than_zero))
具体执行结果如下:
这个filter类似于一个for循环,但它是一个内置函数,并且更快。注意:如果map和filter对你来说看起来并不优雅的话,那么你可以看看其他:列表/字典/元组推导式。
当需要对一个列表进行一些计算并返回结果时,Reduce 是个非常有用的函数。举个例子,当你需要计算一个整数列表的乘积时。通常在 python
中你可能会使用基本的 for 循环来完成这个任务;现在我们来试试 reduce:
from functools import reduce
product = reduce( (lambda x, y: x * y), [1, 2, 3, 4] )
print(product)
具体执行结果如下:
set(集合)是一个非常有用的数据结构。它与列表(list)的行为类似,区别在于set不能包含重复的值。这在很多情况下非常有用。例如你可能想检查列表中是否包含重复的元素,你有两个选择,第一个需要使用for循环,就像这样:
some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
duplicates = []
for value in some_list:
if some_list.count(value) > 1:
if value not in duplicates:
duplicates.append(value)
print(duplicates)
具体执行结果如下:
但还有一种更简单更优雅的解决方案,那就是使用集合(sets),你直接这样做:
some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
duplicates = set([x for x in some_list if some_list.count(x) > 1])
print(duplicates)
具体执行结果如下:
集合还有一些其它方法,下面我们介绍其中一部分。
你可以对比两个集合的交集(两个集合中都有的数据),如下:
valid = set(['yellow', 'red', 'blue', 'green', 'black'])
input_set = set(['red', 'brown'])
print(input_set.intersection(valid))
具体执行结果如下:
你可以用差集(difference)找出无效的数据,相当于用一个集合减去另一个集合的数据,例如:
valid = set(['yellow', 'red', 'blue', 'green', 'black'])
input_set = set(['red', 'brown'])
print(input_set.difference(valid))
具体执行结果如下:
你也可以用符号来创建集合,如:
a_set = {'red', 'blue', 'green'}
print(type(a_set))
具体执行结果如下:
集合还有一些其它方法,我会建议访问官方文档并做个快速阅读。
三元运算符通常在Python里被称为条件表达式,这些表达式基于真(true)/假(not)的条件判断,在Python
2.4以上才有了三元操作。下面是一个伪代码和例子:
condition_is_true if condition else condition_is_false
is_fat = True
state = "fat" if is_fat else "not fat"
它允许用简单的一行快速判断,而不是使用复杂的多行if语句。
这在大多数时候非常有用,而且可以使代码简单可维护。另一个晦涩一点的用法比较少见,它使用了元组,请继续看:
(if_test_is_false, if_test_is_true)[test]
fat = True
fitness = ("skinny", "fat")[fat]
print("Ali is ", fitness)
具体执行结果如下:
这之所以能正常工作,是因为在Python中,True等于1,而False等于0,这就相当于在元组中使用0和1来选取数据。上面的例子没有被广泛使用,而且Python玩家一般不喜欢那样,因为没有Python味儿(Pythonic)。这样的用法很容易把真正的数据与true/false弄混。另外一个不使用元组条件表达式的缘故是因为在元组中会把两个条件都执行,而
if-else 的条件表达式不会这样。例如:
condition = True
print(2 if condition else 1 / 0)
print((1 / 0, 2)[condition])
具体执行结果如下:
这是因为在元组中是先建数据,然后用True(1)/False(0)来索引到数据。 而if-else条件表达式遵循普通的if-else逻辑树,
因此,如果逻辑中的条件异常,或者是重计算型(计算较久)的情况下,最好尽量避免使用元组条件表达式。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。