每一种编程语言,都会有自己函数的语法格式,正是因为我们有了函数,才是我们的代码变得更加模块化,代码的利用率大大增加,尽可能的避免代码的冗余。其实,在所有的编程中函数的作用都是一样的,因为重复代码多,可读性差,所以就有了函数的产生,函数就是封装一个功能。为什么Python好用,就是因为Python内部本身帮我们封装了好多函数。减少了我们对编程的复杂度。Python内部封装好的函数叫内建函数,但是我们自己当然也是可以编写函数的,这叫自定义的函数。
函数的定义和调用
自己来写一个函数:
#函数定义
def area(r):
s = r*r*3.14
return s
area(5)
Python中函数定义的方法:
**定义:def 关键词开头,空格之后接函数名称和圆括号(),最后还有一个":"。
def 是固定的,不能变,他就是定义函数的关键字。
空格 为了将def关键字和函数名分开,必须空
函数名:函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并能表达函数功能
括号:是必须加的,里面是可以带有参数的。比如,我们这个函数就是带参数的(后面再写关于参数的问题),当然,也可以不带参数。
注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。
调用:就是 函数名() ,你有参数就需要在括号里传相应的参数,如果没有就是空,但是必须要写。
**
函数的返回值
return :将值返回给函数的调用者,函数的返回值分为四种:
- 无 return
- return None
- return 1个值 该值是什么,就直接返回给函数的调用者,函数名()
- return 多个值 将多个值放到一个元组里,返回给函数的调用者。
需要注意的说,函数只要遇到return。整个函数就结束了,并给你相对应的返回值,函数值中最好或者说尽量不要输出print语句。
函数的传参
任何一种编程语言中的函数,函数的参数都有两种方式:形参和实参,就拿上面的例子来讲,def area(r):中的r就是形式参数, area(5) 就是实际参数。还有人还是分不清这两个,这么来告诉你,函数中传递的参数就是形参,调用函数时,你用的就是实参。
如果按照参数调用函数的话,那就要一一对应。
def func(a,b,c):
print(a)
print(b)
print(c)
func('fdsafdas',3,4)
动态传参
def func(*args):
print(args)
func(1,2,3,4,5,7,8,9,1,2,3,4,5,6,7,8)
*args 动态参数,你也可以说是万能参数,args接受的就是实参对应的 所有位置参数,并将其放在元组中。
形参对应顺序:
位置参数,*args,默认参数,给个例子:
def func(a,b,c,d,*args,e='男'):
print(a)
print(b)
print(c)
print(d)
print(args)
print(e)
func(1,2,3,4,5,6,7,e='女')
**kwargs 动态传参,他将所有的关键字参数(无意义的)放到一个字典中。
def func(a,b,c,**kwargs):
print(kwargs)
func(1,2,r=4,b1=5,c1=6,c=7,)
def func(a,b,c,d,*args,e='男',**kwargs):
print(a)
print(b)
print(c)
print(d)
print(args)
print(e)
print(kwargs)
func(1,2,3,4,5,6,7,v=3,m=7,h=9,e='女')
那么,给出一个最终的函数的传参顺序:
**位置参数,*args,默认参数,**kwargs
def func1(*args,**kwargs):
pass
func1()
* 魔法运用
def func(*args):
print(args) #(1,2,30,1,2,33.。。。。。)
l1 = [1,2,30]
l2 = [1,2,33,21,45,66]
tu = (1,2,3)
func(1,2,30,1,2,33,21,45,66)
func(*'qweqrfdsaf')
func(*{'name':'alex',"age":12})
func(*l1,*l2)
def func(*args):
print(args)
func(1,2,3,10,20,80)
def func(**kwargs):
print(kwargs)
dic1 = {'name1':'alex','age1':46}
dic2 = {'name':'jj','age':56}
func(**dic1,**dic2)
在函数的调用执行时, *args可迭代对象,代表打散(list,tuple,str,dict(键))将元素一一添加到args。 **kwargs字典,代表打散,将所有键值对放到一个kwargs字典里。
**在函数定义时,*args,**kwargs代表的是聚合。
def func(*args,**kwargs):
print(args)
print(kwargs)
dic1 = {'name1':'alex','age1':46}
dic2 = {'name':'老男孩','age':56}
func(*[1,2,3,4],*'asdfsad',**dic1,**dic2)
def func(*args,**kwargs):
args = (1,2,3,4)
kwargs = {'age': 56, 'name': '老男孩'}
print(*args)
print(**kwargs) #(age = 56 ,name = 老男孩)
dic1 = {'name1':'alex','age1':46}
dic2 = {'name':'老男孩','age':56}
func(*[1,2,3,4],*'asdfsad',**dic1,**dic2)
func(**dic2)
命名空间
每一个变量都有自己的变量存储位置,那么存放名字与值的关系’的空间起了一个名字-------命名空间。
命名空间一共分为三种:
全局命名空间:可以理解为,从开始时就已经创建变量并且贯穿整个代码,我们可以随时调用。
局部命名空间:可以理解为,我们临时创建的变量,它只能在某一个地方调用该变量。
内置命名空间:内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple…它们都是我们熟悉的,拿过来就可以用的方法
三种命名空间之间的加载与取值顺序:
加载顺序
内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
取值顺序
局部调用:局部命名空间->全局命名空间->内置命名空间
全局调用:全局命名空间->内置命名空间
综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻。
作用域
作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。
全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
局部作用域:局部名称空间,只能在局部范围内生效
globals和locals方法
在全局调用globals和locals
print(globals())
print(locals())
在局部调用locals和globals
def func():
a = 12
b = 20
print(locals())
print(globals())
func() ##执行结果:{'a': 12, 'b': 20}
{'__spec__': None, '__file__': 'D:/myPython/data/作用域.py', '__doc__': None, '__builtins__': <module 'builtins' (built-in)>, 'func': <function func at 0x00000241FE045378>, '__name__': '__main__', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000241FDBF8F28>, '__cached__': None, '__package__': None}
global关键字,nonlocal关键字
global:
1,声明一个全局变量。
2,在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)。
def func():
global a
a = 3
func()
print(a)
count = 1
def search():
global count
count = 2
search()
print(count)
需要注意的是,对可变数据类型(list,dict,set)可以直接引用不用通过global
li = [1,2,3]
dic = {'a':'b'}
def change():
li.append('a')
dic['q'] = 'g'
print(dic)
print(li)
change()
print(li)
print(dic)
nonlocal
1,不能修改全局变量。
2,在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
def add_b():
b = 42
def do_global():
b = 10
print(b)
def dd_nonlocal():
nonlocal b
b = b + 20
print(b)
dd_nonlocal()
print(b)
do_global()
print(b)
add_b()
函数的嵌套和作用域链
函数的嵌套调用
def max2(x,y):
m = x if x>y else y
return m
def max4(a,b,c,d):
res1 = max2(a,b)
res2 = max2(res1,c)
res3 = max2(res2,d)
return res3
max4(23,-7,31,11)
函数的嵌套定义
def f1():
print("in f1")
def f2():
print("in f2")
f2()
f1()
##执行结果:in f1
in f2
def f1():
def f2():
def f3():
print("in f3")
print("in f2")
f3()
print("in f1")
f2()
f1()
执行结果:
in f1
in f2
in f3
函数的作用域链
小范围作用域可以使用大范围的变量,但是反之不行,他是单向的。
def f1():
a = 1
def f2():
def f3():
print(a)
f3()
f2()
f1()
################
def f1():
a = 1
def f2():
a = 2
f2()
print('a in f1 : ',a)
f1()
函数名的本质
函数名本质上就是函数的内存地址。
1.可以被引用
def func():
print('in func')
f = func
print(f)
2.可以被当作容器类型的元素
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
l = [f1,f2,f3]
d = {'f1':f1,'f2':f2,'f3':f3}
#调用
l[0]()
d['f2']()
3.可以当作函数的参数和返回值
def f1():
print('f1')
def func1(argv):
argv()
return argv
f = func1(f1)
f()
第一类对象(first-class object)指
1.可在运行期创建
2.可用作函数参数或返回值
3.可存入变量的实体。
闭包
def func():
name = ’ luckfariy ’
def inner():
print(name)
闭包函数
内部函数包含对外部作用域而非全局作用域变量的引用,该内部函数称为闭包函数
函数内部定义的函数称为内部函数
由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了。如果我们非得要拿到?那就返回呀!函数内的变量我们要想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?是不是直接就把这个函数的名字返回就好了?
这才是闭包函数最常用的用法
def func():
name = ' luckfariy '
def inner():
print(name)
return inner
f = func()
f()
判断闭包函数的方法__closure__
#输出的__closure__有cell元素 :是闭包函数
def func():
name = ' luckfariy '
def inner():
print(name)
print(inner.__closure__)
return inner
f = func()
f()
#输出的__closure__为None :不是闭包函数
name = ' luckfariy '
def func2():
def inner():
print(name)
print(inner.__closure__)
return inner
f2 = func2()
f2()
闭包嵌套
def wrapper():
count = 1000
def func():
name = ' luckfariy '
def inner():
print(name,count)
return inner
return func
f = wrapper()
i = f()
i()
闭包函数获取网络应用
from urllib.request import urlopen
def index():
def get():
return urlopen(url).read()
return get
luckfariy = index()
content = luckfariy ()
print(content)