1. 什么是函数
函数是对程序逻辑进行结构化或过程化的一种编程方法.
而过程区别于函数在于:过程是简单,特殊,没有返回值的函数,而python的过程就是函数,因为返回默认值None.
1. 返回值与函数类型
python中函数一定存在返回值,没有明确返回的,则默认返回None:
>>> def hello(): print("hello world") >>> res = hello()hello world>>> res>>> print(res)None>>> type(res)
而如果函数返回的对象数目是一个,则返回一个object,如果是多个,则返回一个tuple(元组):
>>> def hello(): ss = "hello" return ss>>> res = hello()>>> res'hello'>>> type(res)>>> def func(): return ("hello", 1, [2, 3, 4])>>> ss = func()>>> ss('hello', 1, [2, 3, 4])>>> type(ss)
2. 调用函数
一般通过函数操作符()来调用函数,但是对于多个参数的情况下,使用关键字为更好的编码风格:
>>> def net_conn(host = "127.0.0.1", port = 80): print("host = %s, port = %d" % (host, port)) >>> net_conn(host = "192.168.0.1", port = 8080)host = 192.168.0.1, port = 8080>>> net_conn(port = 8080, host = "192.168.0.1")host = 192.168.0.1, port = 8080>>> net_conn(port = 8080)host = 127.0.0.1, port = 8080更复杂的情况是:我们可以使用参数组:
func(positional_args, keyword_args, *tuple_grp_nonkw_args, **dict_grp_kw_args)positional_args:标准的位置参数
keyword_args:关键字参数
tuple_grp_nonkw_args:元组形式体现的非关键字参数组
dict_grp_kw_args:装有关键字参数的字典
一般来说,函数的调用参数最好只能有以下四种方式(主要混淆的地方是:关键字参数和字典会混淆,所以都是用的时候,最好不要使用关键字,看第四种方式):
第一种方式:只有标准参数和关键字参数:
func(positional_args, keyword_args)第二种方式:只有标准参数和元组:
func(positional_args, *tuple_grp_nonkw_args)
>>> def func(name, *num): print("name is %s" % name) print("num is") print(num) >>> func("lcj", 1, 2, 3)name is lcjnum is(1, 2, 3)备注:这里不要自作聪明的调用func("lcj", (1, 2, 3)),实际上这样元组只有一个参数:
>>> func("lcj", (1, 2, 3))name is lcjnum is((1, 2, 3),)第三种方式:只有标准参数和字典
>>> def func(name, **dictArgs): print("name is %s" % name) for key, value in dictArgs.items(): print("key is %s, value is %s" % (key, value))>>> func("lcj", x = 1, y = 2)name is lcjkey is x, value is 1key is y, value is 2
第四种方式:全部使用,但是关键字中默认值不提供:
>>> def func(num, age=25, name="lcj", *tupleArgs, **dictArgs): print("num is:%d" % num) print("name is:%s, age is:%d" % (name, age)) print("tuple Args is") print(tupleArgs) for key, value in dictArgs.items(): print("key is:%s, value is:%s" % (key, value))>>> func(123, 23, "voler", 1, 2, 3, x = 1, y = 2)num is:123name is:voler, age is:23tuple Args is(1, 2, 3)key is:y, value is:2key is:x, value is:1
3. 创建函数
1. def语句
def function_name(arguments): "function_documentation_string" function_body_suite标题行由def关键字,函数的名字,以及参数的集合组成.剩余部分包括了一个可选但是强烈推荐的文档字符串和必须的函数体
2. 声明与定义/前向引用
python中声明和定义是为一体的,这导致了python的一种很有趣的特性是(和JavaScript一样):函数会先完成其定义,然后才会执行函数体:
>>> def foo(): print("in foo") bar() >>> def bar(): print("in bar") >>> foo()in fooin bar
3. 函数属性
一般我们都是通过dir函数来进行查看:
>>> def foo(): """this is a function for testing""" print("hello world") >>> dir(foo)['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']>>> foo.__doc__'this is a function for testing'>>> help(foo)Help on function foo in module __main__:foo() this is a function for testing
4. 内部/内嵌函数
函数内定义函数完全是合法的,并且内部函数的作用域就是其外部函数:
>>> def foo(): def bar(): print("bar() called") print("foo() called") bar() >>> foo()foo() calledbar() called
5. 函数(与方法)装饰器
装饰器背后的主要动机源自python面向对象编程.装饰器是在函数调用之上的修饰,这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用.
装饰器的语法以@开头,接着是装饰器函数的名字和可选的参数.紧接着装饰器声明的是被修饰的函数和装饰函数的可选参数:
@decorator(dec_opt_args)def func(func_opt_args): //do_something而更普遍的是堆叠的装饰器
@deco2@deco1def func(arg1, ...) : pass其等价于:
def func(arg1, ...) : passfunc = deco2(deco1(func))
1. 有参数和无参数的装饰器
没有参数的情况下:
@decodef foo() : pass等价于:
foo = deco(foo)而存在参数的情况下:
@deco(deco_args)def foo(): pass等价于:
foo = deco(deco_args)(foo)无参数的例子如下:
def show(func): print(func) def oneFunc(name): print(name) print(oneFunc) print(func.__name__) print(oneFunc.__name__) return oneFunc@showdef func(name): return name#这里,func非原先的func了,而是等价于:show(func),所以func == oneFunc#之所以show函数需要传递进去一个func参数,一是因为语法的要求(show(func)中func是作为参数的)#二是因为show中可能用到func的一些数据#因为使用装饰器后,实际上是产生一个新的函数show(func),而非原先的函数funcfunc("hello")print(func)而输出如下:
>>>而有参数的原理是一样的:.oneFunc at 0x000000000344D048>funconeFunchello .oneFunc at 0x000000000344D048>
def show(num): def oneFunc(func): def childFunc(name): num.append(name) print("num is") print(num) return childFunc return oneFunc@show([1, 2, 3])def func(name): return namefunc("hello")而输出如下
>>> num is[1, 2, 3, 'hello']
4. 传递函数
函数本身可以理解为一个变量,传递函数本身就是传递一个引用而已:
>>> def foo(): print("hello world") >>> bar = foo>>> bar()hello world>>> id(foo)15977464>>> id(bar)15977464
5. 函数式编程
1. 匿名函数与lambda
lambda的语法如下:
lambda [arg1, [, arg2, ...argN]] : expressionpython允许用lambda关键字创造匿名函数,匿名是因为不需要以标准的方式来声明.
>>> (lambda x, y : x * y)(2, 3)6
2. 内建函数apply(), filter(),map(),reduce()
内建函数 | 描述 |
apply(func[,nkw][,kw]) | 用可选的参数来调用func,nkw为非关键字参数,kw为关键字参数:返回值是函数调用的返回值 |
filter(func, seq) | 调用一个布尔函数func来迭代遍历每个seq中的元素:返回一个使func返回值为true的元素的序列 |
map(func, seq1[,seq2...]) | 将函数func作用域给定序列(s)的每个元素,并用一个列表来提供返回值:如果func为None,func表现为一个身份函数,返回一个含有每个序列中元素集合的n个元组的列表 |
reduce(func, seq[,init]) | 将二元函数作用于seq序列的元素,每次携带一对(先前的结果以及下一个序列元素),连续的将现有的结果和下一个值作用在获得的随后的结果上,最后减少我们的序列为一个单一的返回值:如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素 |
apply():已经被淘汰
filter():
>>> from random import randint>>> allNums = [randint(1, 99) for i in range(9)]>>> allNums[29, 3, 67, 81, 13, 36, 64, 92, 4]>>> print(list(filter(lambda x : x % 2, allNums)))[29, 3, 67, 81, 13]map():
>>> print(list(map(lambda x, y : x + y, (1, 2, 3), (4, 5, 6))))[5, 7, 9]reduce():
>>> import functools>>> print(functools.reduce(lambda x, y : x + y, range(5)))10
3. 偏函数应用
currying的概念将函数式编程的概念和默认参数以及可变参数结合在一起.一个带n个参数,curried的函数固化第一个参数为固定参数,并返回另一个带n-1个参数函数对象:
>>> from operator import add, mul>>> from functools import partial>>> add1 = partial(add, 1)>>> mul100 = partial(mul, 100)>>> add1(100)101>>> mul100(2)200
6. 变量作用域
声明适用的程序的范围被称为声明的作用域.在一个过程中,如果名字在过程的声明之外,它的出现即为过程的局部变量;否则的话,出现即为非局部变量:
>>> global_str = "foo">>> def foo(): local_str = "bar" return global_str + local_strglobal_str是全局变量,而local_str是局部变量.foo()函数可以对全局和局部变量进行访问,而代码的主题部分只能访问全局变量.
而如果我们需要在局部变量中明确的引用已命名的全局变量,则需要关键字global:
>>> global_str = "foo">>> def foo(): global global_str print(global_str) global_str = "bar" print(global_str) >>> foo()foobar>>> global_str'bar'
1. 闭包
闭包将内部函数自己的代码和作用域以及外部函数的作用结合起来.闭包的词法变量不属于全局名称空间域或者局部的--而属于其他的名称空间,带有"流浪"的作用域.(注意这不同于对象因为那些变量是存活在一个对象的名称空间但是闭包变量存活在一个函数的名称空间和作用域)
>>> def counter(start_at): def incr(): start_at[0] += 1 return start_at[0] return incr>>> count = counter([5])>>> print(count())6>>> print(count())7备注:这里之所以用列表因为列表可变,不能使用不可变的数据,如数值,字符串等等.
7. 生成器
从语法上讲,生成器是一个带yield语句的函数.一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果--那就是yield语句的功能,返回一个值给调用者并暂停执行,当生成器的next()方法被调用的时候,它会准确的从离开地方继续.
>>> def simpleGen(): yield 1 yield "2-->punch!" >>> for i in simpleGen(): print(i) 12-->punch!而我们可以通过send来向生成器传递参数,用close来退出生成器:
>>> def counter(start_at = [0]): while True: val = (yield start_at[0]) if val is not None: start_at[0] = val else: start_at[0] += 1>>> count = counter([5])>>> print(count.__next__())5>>> print(count.__next__())6>>> count.send(9)9>>> count.__next__()10