python cookbook阅读之——7. 函数

def语句定义的函数是所有程序的基石。

7.1 编写可接受任意数量参数的函数

(1)可接受任意数量的位置参数的函数,使用*开头的参数。例如:

>>> def avg(first, *rest):
...     return (first + sum(rest)) / (1 + len(rest))
... 
>>> avg(1,2)
1.5
>>> avg(1,2,3,4)
2.5

这个示例中,rest是一个元祖,它包含了其他所有传递过来的位置参数。代码在之后的计算中会将其视为一个序列来处理。

(2)接受任意数量的关键字参数,使用**开头的参数。例如:

>>> import html
>>> def make_element(name, value, **attrs):
...     keyvals = [' %s="%s"' % item for item in attrs.items()]
...     attr_str = ''.join(keyvals)
...     element = '<{name}{attrs}>{value}</{name}>'.format(name=name, attrs=attr_str, value=html.escape(value))
...     return element
... 
>>> make_element('item','Abcd', size='large', quet=6)
'<item size="large" quet="6">Abcd</item>'
>>> make_element('div', '小试牛刀', style='color:red;')
'<div style="color:red;">小试牛刀</div>'

这里的attrs是一个字典,它包含了所有传递过来的关键字参数。

(3)函数能同时接受任意数量的位置参数和关键字参数,只要联合使用*和**即可。例如:

>>> def anyargs(*args, **kwargs):
...     print(args)     #tuple
...     print(kwargs)   #dict
... 
>>> anyargs(2,3,4,param1="a",param2="b")
(2, 3, 4)
{'param1': 'a', 'param2': 'b'}

这个函数中所有的位置参数都放置在元祖args中,所有的关键字参数都会放置在字典kwargs中。

总结:在函数定义中,以*打头的参数只能作为最后一个位置参数出现,而以**打头的参数只能作为最后一个参数出现。(在*打头的参数后仍然可以有其他参数出现,但是出现在*打头的参数后,只能作为关键字参数使用)例如:

>>> def anyargs(x, *args, y, **kwargs):
...     print(args)
...     print(kwargs)
...     print(x)
...     print(y)
... 
>>> anyargs(2,3,4,param1="a",y="1,2",param2="b")
(3, 4)
{'param1': 'a', 'param2': 'b'}
2
1,2
>>> anyargs(2,3,4,y="1,2",param1="a",param2="b")
(3, 4)
{'param1': 'a', 'param2': 'b'}
2
1,2

7.2 带参数注解的函数,增加代码可读性。函数注解会保存在__annotations__属性中。例如:

>>> def addInt(x:int, y:int) -> int:
...     return x + y
... 
>>> addInt(2,9)
>>> addInt.__annotations__
{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

7.4 从函数中返回多个值

>>> def myfun():  
...     return 1,2,3   #实际上返回的是一个元祖
... 
>>> a,b,c = myfun()   #元祖解包赋值给变量
>>> a
1
>>> b
2
>>> c
3
>>> d = myfun()   #代表了整个元祖
>>> d
(1, 2, 3)

7.5 定义默认参数

默认参数赋值,应该总是不可变的对象,例如:None、True、False、数字、或者字符串。特别要注意,绝对不要编写这样的代码:

def fun(a, b=[]):  # NO!  会陷入各种麻烦中,默认值在函数体外被修改时,调用会产生持续影响
    ...

#None会被判定为False,还有长度为0的字符串,列表,元祖,字典 也会被判断为False
>>> def fun(a, b=None):
...     if not b:   # NO! 可以使用 b is None  代替这句
...             b = []
... 
>>> 

7.6 定义匿名或内联函数

>>> b = lambda x,y: x + y     #等价于 def b(x, y)
>>> b(3, 4)
7
>>> def b(x, y):
...     return x + y
... 
>>> b(3, 4)
7

下面lambda表达式中x是一个自由变量,此实例可以验证,lambda在运行时才进行绑定,而不是定义的时候绑定(与def不同)

>>> x = 10
>>> a = lambda y: x + y
>>> a(10)
20
>>> x = 20
>>> a(10)
30

再来看一下易出错的循环迭代,示例如下:

>>> funcs = [lambda x: x+n for n in range(5)] #执行时候n始终为最后一次的赋值
>>> for f in funcs:
...     print(f(0))
... 
4
4
4
4
4
>>> funcs = [lambda x, n=n: x+n for n in range(5)]   #执行时候n每次赋值
>>> for f in funcs:
...     print(f(0))
... 
0
1
2
3
4

7.7 访问定义在闭包中的变量

通过函数来扩展闭包,使得闭包内层定义的变量可以背访问和修改。一般来说,在闭包内层定义的变量对于外界来说是完全隔离的。但是可以通过编写来存取函数,并将它们作为函数属性附加到闭包上来提供对内层变量的访问。例如:

>>> def sample():
...     n = 0
...     def fun():
...             print('n = ', n)
...     def get_n():
...             return n
...     def set_n(value):
...             nonlocal n     #nonlocal 声明使得编写函数来修改内层变量成为可能
...             n = value
...     fun.get_n = get_n     #函数属性能够将存取函数以直接的方式附加到闭包函数上
...     fun.set_n = set_n
...     return fun
... 
>>> f = sample()
>>> f()
n =  0
>>> f.set_n(10)
>>> f()
n =  10
>>> f.get_n()
10

闭包模拟成类实例,速度比class定义的实例,测试出大约快8%,测试中的大部分时间都花在对实例变量的直接访问上,闭包要更快一些,这是因为不用涉及额外的self变量。

>>> import sys
>>> class aaa():
...     def __init__(self, locals = None):
...             if locals is None:
...                     locals = sys._getframe(1).f_locals
...             self.__dict__.update((key,value) for key, value in locals.items() if callable(value))
...     def __len__(self):
...             return self.__dict__['__len__']()
... 
>>> def Stack():   #闭包模拟成类实例
...     items = []
...     def push(item):
...             items.append(item)
...     def pop():
...             items.pop()
...     def __len__():
...             return len(items)
...     return aaa()
... 
>>> s = Stack()
<__main__.aaa object at 0x7f89f34773c8>
>>> s.push(10)
>>> s.push(20)
>>> s.push('hello')
>>> len(s)
3
>>> s.pop()  #pop出'hello'
>>> len(s)
2
>>> s.pop()  #pop出20
>>> s.pop()  #pop出10
>>> len(s)
0
>>> class Stack2:      #实例
...     def __init__(self):
...             self.items = []
...     def push(self, item):
...             self.items.append(item)
...     def pop(self):
...             return self.items.pop()
...     def __len__(self):
...             return len(self, items)
... 
#性能对比
>>> from timeit import timeit
>>> s = Stack()
>>> timeit('s.push(1);s.pop()', 'from __main__ import s')   #闭包耗用时间
0.3924146047793329
>>> s = Stack2()
>>> timeit('s.push(1);s.pop()', 'from __main__ import s')   #实例耗用时间
0.4104466247372329
>>> 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值