1. 関数の紹介
なぜ関数があるのですか?なぜなら、コードを書くときに関数がないと、繰り返しコードが多くなり、コードの再利用率が比較的低くなってしまうからです。 。 。また、このようなコードは保守が非常に困難です。これらの問題を解決するために、頻繁に発生するコードをカプセル化して、このコードを呼び出す必要がある場所でこの関数を呼び出すことができるようになりました。
関数の定義: 関数は、名前 (関数名) によってカプセル化された一連のステートメントを指します。この関数を実行するには、その関数名を呼び出すだけです
特徴:
コードの再利用
一貫性のあるパフォーマンス
拡張性
2. 関数の作成
Pythonでの関数定義の形式は以下の通りです:
def 函数名(形参): 函数体内部代码块
関数の呼び出し 関数名(実引数)を使用して関数を呼び出すことができます。
関数名の命名規則は変数の命名規則と同じです:
関数名はアンダースコアまたは文字で始まる必要があり、文字、数字、またはアンダースコアの任意の組み合わせを含めることができます。句読点は使用できません。
関数名では大文字と小文字が区別されます。
関数名に予約語は使用できません。
仮パラメータと実際のパラメータの違い:
関数が定義されるとき、これらのパラメータは仮パラメータと呼ばれます。これらは正式なパラメータであり、単なるコード名です。
実パラメータは、関数を呼び出すときに関数名の後ろにある括弧内のパラメータです。仮パラメータと実パラメータは 1 対 1 に対応している必要があります。そうでない場合、関数呼び出し時にエラーが報告されます。
3. 関数のパラメータと戻り値
前述のように、関数の仮パラメータと実パラメータは 1 対 1 に対応する必要があるため、パラメータの対応は次のとおりです。キーワードパラメータ
デフォルトパラメータ
可変長パラメータ*args
可変長パラメータ**kwargs
例:
def f(name,age): print(name,age) f("小明",18)
キーワードパラメータは、関数を呼び出すときに、特定のパラメータが特定のキーワードに属することを宣言する概念です。キーワード引数を使用すると、関数を宣言時とは異なる順序で呼び出すことができます。これは、Python インタープリターがパラメーター名とパラメーター値を照合できるためです。
例:
def f(name,age):
print(name,age)
f(name="小明",18)
デフォルトパラメータは、関数が宣言されるときにパラメータのデフォルト値を指定できます。そのようなパラメータはデフォルト値パラメータと呼ばれます。関数の呼び出し時にデフォルト パラメータが対応する実際のパラメータを受け取らない場合、デフォルト値がこのパラメータに割り当てられます。
例:
def f(name,age,sex="male"):
print(name,age,sex)
f(name="小明",18)
このようにして、デフォルトパラメータ男性が性別に割り当てられます。
4. 不定の長さのパラメータ *argsPython では、関数が宣言されるときに、パラメータで (*変数名) を使用して不確実な長さのパラメータを受け入れることができますが、Python では慣例により、誰もが * を使用します。可変長パラメーターなので、関数呼び出し時に渡されるパラメーターは可変長にすることができます。 args が可変長パラメーターを受け入れた後、これらのパラメーターはタプルに入れられます。これらの可変長パラメーターは、args にアクセスすることで取得できます。
例:
def f(*args):
print(args)
f("小明",18,"male")
出力されるのは、3 つの要素 ("Xiao Ming"、18、"male") を格納するタプルです。
5. 可変長パラメータ**kwargsしかし、上記の引数は名前のないパラメータしか受け取れないので、キーワードパラメータと同様の可変長パラメータがある場合はどうなるでしょうか? Python は (**変数名) を使用して、可変長の名前付き変数パラメーターを受け取ります。同様に、Python では **kwargs を使用して可変長の名前付きパラメーターを受け取るのが通例です。 kwargs は可変長パラメーターを受け取ると、これらのパラメーターを辞書に入れ、対応するパラメーター値をキーを通じて取得できます。
例:
def f(**kwargs):
print(kwargs)
f(name="小明",age=18,sex="male")
関数が上記のタイプのパラメータをすべて使用する場合、どうすればよいでしょうか?あいまいさを避けるために、Python では、複数のパラメーターが混在している場合、次の順序使用規則に従うことを規定しています:
def f (必須パラメーター、デフォルトパラメーター、*args、**kwargs):
passIf Both If args and kwargs が存在し、引数は左側にありますデフォルトのパラメータは必須パラメータの右側、*args の左側にあります
キーワードパラメータの位置は固定されていません(追記: キーワードパラメータは関数の定義時に決定されません)
那么,假如有一个列表想要传递进入一个不定长的未命名参数的函数中去,可以在该列表前面加上*实现,同理如果想传递一个字典进入不定长命名参数的函数中去,可以在该字典前面加上**
举个栗子:
def f(*args,**kwargs): print(args) for i in kwargs: print("%s:%s"%(i,kwargs[i])) f(*[1,2,3],**{"a":1,"b":2})
函数的返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
函数在执行过程中只要遇到return语句,就会停止执行并返回结果,也可以理解为 return 语句代表着函数的结束 如果未在函数中指定return,那这个函数的返回值为None
return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出。
4.LEGB作用域
python中的作用域分4种情况:
L:local,局部作用域,即函数中定义的变量;
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
G:globa,全局变量,就是模块级别定义的变量;
B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
local和enclosing是相对的,enclosing变量相对上层来说也是local。
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)不会引入新的作用域。
变量的修改(错误修改,面试题里经常出):
x=6 def f2(): print(x) x=5 f2() # 错误的原因在于print(x)时,解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错: # local variable 'x' referenced before assignment.如何证明找到了x=5呢?简单:注释掉x=5,x=6 # 报错为:name 'x' is not defined #同理 x=6 def f2(): x+=1 #local variable 'x' referenced before assignment. f2()
global关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:
count = 10 def outer(): global count print(count) count = 100 print(count) outer()
nonlocal关键字
global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了
def outer(): count = 10 def inner(): nonlocal count count = 20 print(count) inner() print(count) outer()
小结
变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;
只有模块、类、及函数才能引入新作用域;
对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;
内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。
5.特殊函数
递归函数定义:递归函数就是在函数内部调用自己
有时候解决某些问题的时候,逻辑比较复杂,这时候可以考虑使用递归,因为使用递归函数的话,逻辑比较清晰,可以解决一些比较复杂的问题。但是递归函数存在一个问题就是假如递归调用自己的次数比较多的话,将会使得计算速度变得很慢,而且在python中默认的递归调用深度是1000层,超过这个层数将会导致“爆栈”。。。所以,在可以不用递归的时候建议尽量不要使用递归。
举个栗子:
def factorial(n): # 使用循环实现求和 Sum=1 for i in range(2,n+1): Sum*=i return Sum print(factorial(7)) def recursive_factorial(n): # 使用递归实现求和 return (2 if n==2 else n*recursive_factorial(n-1)) print(recursive_factorial(7)) def feibo(n): # 使用递归实现菲波那切数列 if n==0 or n==1:return n else:return feibo(n-1)+feibo(n-2) print(feibo(8)) def feibo2(n): # 使用循环实现菲波那切数列 before,after=0,1 for i in range(n): before,after=after,before+after return before print(feibo2(300))
递归函数的优点:定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
递归特性:
必须有一个明确的结束条件
每次进入更深一层递归时,问题规模相比上次递归都应有所减少
递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)
6.函数式编程
关于函数式编程,我理解的也不是很深,但是python中有4个比较重要的内置函数,组合起来使用有时候能大大提高编程效率。
1 filter(function, sequence) str = ['a', 'b','c', 'd'] def fun1(s): if s != 'a': return s ret = filter(fun1, str) print(list(ret))# ret是一个迭代器对象
对sequence中的item依次执行function(item),将执行结果为True的item做成一个filter object的迭代器返回。可以看作是过滤函数。
2 map(function, sequence)
str = [1, 2,'a', 'b'] def fun2(s): return s + "alvin" ret = map(fun2, str) print(ret) # map object的迭代器 print(list(ret))# ['aalvin', 'balvin', 'calvin', 'dalvin']
对sequence中的item依次执行function(item),将执行结果组成一个map object迭代器返回. map也支持多个sequence,这就要求function也支持相应数量的参数输入:
def add(x,y): return x+y print (list(map(add, range(10), range(10))))##[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
3 reduce(function, sequence, starting_value)
from functools import reduce def add1(x,y): return x + y print (reduce(add1, range(1, 101)))## 4950 (注:1+2+...+99) print (reduce(add1, range(1, 101), 20))## 4970 (注:1+2+...+99+20)
对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用.
4 lambda
普通函数与匿名函数的对比:
#普通函数 def add(a,b): return a + b print add(2,3) #匿名函数 add = lambda a,b : a + b print add(2,3) #========输出=========== 5 5
匿名函数的命名规则,用lamdba 关键字标识,冒号(:)左侧表示函数接收的参数(a,b) ,冒号(:)右侧表示函数的返回值(a+b)。
因为lamdba在创建时不需要命名,所以,叫匿名函数
更多python常用函数详解相关文章请关注PHP中文网!