Python函数的基础知识

函数是一段具有特点功能的、可重用的语句组。(将代码封装起来) 

定义:def(定义一个函数)关键词开头,空格之后接函数名称和圆括号(),最后还有一个“:”。

   def是固定的,不能变,必须是连续的def三个字母,不能分开。

   空格  为了将def关键字和函数名称分开,必须空。

   函数名:函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短并表达函数功能

   括号:必须要有;

注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性

调用:就是函数名()一定要加上括号

#函数定义
def my_len():
    """计算s1的长度"""
    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    print(length)

#函数调用  
my_len()
函数的定义和调用
注:只定义函数而不调用,函数就不执行。

函数的返回值

在调用python自带的len函数时,必须用一个变量来接收这个值。

str_len=len('hello,word')

使用自己写的函数也可以做到这一点

# 函数定义
def my_len():
    s1='hello world'
    length=0
    for i in s1:
        length=length+1
    print(length)
str_len=my_len()  #函数调用
print('str_len:%s'%str_len)
# 11
# str_len:None  说明这段代码什么都没有返回。

在写函数的时候,要尽量以功能为向导,结果最好不要直接在函数中打印出来。

关键字return的作用

1、返回一个值

2、终止一个函数的继续

def my_len():  # 函数名的定义
    s1='hello world'
    length=0
    for i in s1:
        length=length+1
    return length  # 函数的返回值
str_len=my_len()  #函数的调用以及返回值的接收
print(str_len)
# 11

在没有返回值的时候:

1、不写return与写入return None的效果相同,返回的只都是None          

2、只写一个return后面不加任何东西的时候与写return None的效果一样

返回多个值:

1、当用一个变量接收返回值的时候,收到的是一个元组。这是因为在python中把用逗号分割的 多个值认为是一个元组。

2、当返回值有多个变量接收,那么返回值的个数应该和接收变量的个数完全一致。

3. return还有一个特殊的用途,一旦执行到return,后面的语句就不在执行了(结束一个函数)。(和break类似但有区别,break是跳出循环,如果循环后有代码则继续执行。return是结束整个函数)

4.如果在函数中有多个return,只执行第一个return。

 

###########
def func():
    return "a" , "b"   #返回多个值时接收到的是一个元组
c = func() # c接收的是一个元组
print(c)
##('a', 'b')
#返回多个值,用多个变量接收(接收的变量数与返回值的个数要一致)
def func():
    return "a" , "b" , "c"
d , e , f = func()
print(d , e , f)
##a b c

def func():
    return [1,2,3]
a , b , c = func() #列表和元组是可以解包的
print(a,b,c)
##1 2 3

返回的字典类型有点意外:
def func():
return {"name":"span"}
dic = func() #需要字典类型来接收,而不能直接用k,v,字典解包解出来的只是键
print(dic)
##{'name': 'span'}
 
##return扩展
def f(L):  #L为形式参数,接收参数
    if len(L)>4:
        return True
    else:
        return False
s=[1,2,3,4]
dic={5,6,7,8,9}
print(f(s)) #s为实际参数,给参数的过程就是传参(argument)
print(f(dic))
# False
# True

函数的参数:

#函数定义
def fun(s):
    count=0
    for i in s:
        count+=1
    return count
#函数调用
str=fun('jshdjkshkdhsk')
print(str)
#13
在上述代码中,告诉了fun函数要计算的字符串是谁,这个过程就是传递参数,简称传参;在调用函数时传递的这个'jshdjkshkdhsk'和定义函数时的s就是参数。

实参与形参:

在调用函数时传递的'jshdjkshkdhsk'被称为实际参数,因为这是实际要交给函数的内容,简称实参

定义s的时候,s只是一个变量的名字,被称为形式参数,因为在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参

在传递多个参数:多个函数分别可以使用任意数据类型

按照关键字传参数和按照位置传参数是可以混用的,但是首先都是按位置传,之后再是按关键字传的;按照位置传完该接收的参数只能接收一个值,不接受或者重复接收。

 

 

###传递两个参数的情况
def my_sum(a,b):#有两个参数就该传两个参数,1对应a;2对应b (这样的参数就称为位置参数)
    res = a + b
    return res
ret = my_sum(1,2)
print(ret)
#3
#站在传参(实参)的角度
#按照位置传参
#按照关键字传参
#混着用也可以,但必须是先按照位置传参,再按照关键字传参
   #不能给一个变量传多个值
def my_sum(a,b):
    print(a,b)
    res = a + b
    return res

ret = my_sum(1,b=2)
print(ret)
##
1 2
3

def my_sum(a,b):
    print(a,b)
    res = a + b
    return res

ret = my_sum(b=2,a=1)
print(ret)

##
1 2
3

 

 


 

###站在形参的角度上:

  #位置参数:必须传(有几个参数就传几个,不能多传和少传)

  #默认参数,可以不传,如果不传就按默认的参数,如果传了就按传了的值

####在调用函数的时候:

  #若按照位置传,直接写参数的值

  #若按照关键字传,关键字=值

###定义函数的时候

  #位置参数

  #默认参数,关键字参数。 参数名=值

  #在定义参数时必须先定义位置参数再定义默认参数

  #动态参数(*args),可以接收任意多个参数,组织成一个元组

  #动态参数(**kwargs),接收的是按照关键字传参的值,组织成一个字典

  #args必须在kwargs之前

######接收参数的先后顺序:位置参数、*args、默认参数、**kwargs#############

 ###默认参数:是可以不传的参数,在不传参数的情况下可以使用默认值;如果传了,就会使用传的值

def classmate(name,sex=''):
    print('姓名:%s,性别:%s' %(name,sex))
classmate('张三')
classmate('李四')
classmate('翠花','')

# 姓名:张三,性别:男
# 姓名:李四,性别:男
# 姓名:翠花,性别:女

默认参数 魔性用法:默认参数尽量避免使用可变数据类型(默认参数的陷阱)

def func(L=[]):  # 相当于在def之前先定义了一个str=[],再将str赋值给L,如果不传参数就共用这个数据类型
    L.append(2)
    print(L)
func()
func()
func()
func()

# [2]
# [2, 2]
# [2, 2, 2]
# [2, 2, 2, 2]

def fun(L=[]):  # 相当于在def之前先定义了一个str=[],再将str赋值给L
    L.append(2)
    print(L)
fun([])
fun([])
fun([])
fun([])
#[2]
# [2]
# [2]
# [2]

##################################
def func(k,d={}): #参数有可变数据类型如列表、字典等,如果不给可变数据类型赋值,则一直用的是同一个列表或字典
    d[k] = "V"
    print(d)

func(1)
func(2)
func(3)
###
{1: 'V'}
{1: 'V', 2: 'V'}
{1: 'V', 2: 'V', 3: 'V'}

动态参数:

1、*args:接收所有按照位置传的参数。

#动态参数def func(*args):  # 在参数前面加个*,这个参数就变成了动态参数
def func(*args):
    print(args)  # 使用的时候,所有接收过来的参数都被组织成一个元组的形式
func(2,3,4,'span',[1,2,3])
##(2, 3, 4, 'span', [1, 2, 3])


###*args只能接收按位置传参,按照关键字传参就接收不到
def func(*args,L=[]):
    print(args,L)  # 使用的时候,所有接收过来的参数都被组织成一个元组的形式
func(2,3,4,'span',L=[1,2,3])
##(2, 3, 4, 'span') [1, 2, 3]

#位置参数  动态参数  默认参数
def func(a,b,c,*args,key='key'):  # 在参数前面加个*,这个参数就变成了动态参数
    print(a,b,c)
    print(key)
    print(args)  # 使用的时候,所有接收过来的参数都被组织成一个元组的形式
func(2,3,4,'span',[1,2,3],'xiaoming')

##
2 3 4
key
('span', [1, 2, 3], 'xiaoming')

##累加器

def sum(*args): ##多个动态参数累加
    res = 0
    for i in args:
        res += i
    return res

ret = sum(3,4,5)
print(ret)
#12

 2、**kwargs:按照所有接收关键字传的参数。 

def func(**kwargs):
    print(kwargs) 
func(a=1,b=2,c=3)
func(a=100)
##
{'a': 1, 'b': 2, 'c': 3}
{'a': 100}

##无敌用法: 

def func(*args,**kwargs): #能接收无限多个位置参数和关键字参数
    print(args,kwargs)
func(1,2,3,a=1,b=2,c=3)   #必须先传位置参数再传关键字参数
##
(1, 2, 3) {'a': 1, 'b': 2, 'c': 3}

######接收参数的先后顺序:位置参数、*args、默认参数、**kwargs#############

## *args的魔性用法:

def sum(*args):  # 站在形参的角度上,*args的作用是聚合,将传入的参数转变成元组的形式
    print(args)
    sun=0
    for i in args:
        sun+=i
    return sun
L=[1,2,34,5]
print(sum(*L))  # 站在实参的角度上,*L是将列表L按顺序打散之后变成将列表中的每个元素分别输出
##
(1, 2, 34, 5)
42

##**kwargs的魔性用法:

def func(**kwargs):#聚合
    print(kwargs)
func(c
=3,d=5) dic={'a':1,'b':4} func(**dic) #打散 # {'c': 3, 'd': 5} # {'a': 1, 'b': 4}

 ####三元运算符

 三元运算符 
a=1
b=7
c=0
if a>b:
    c=a
else:
    c=b
print(c)

#与以下的代码的作用效果相同(三元运算表达式)
c=0
a=1
b=7
c=a if a>b else b  #如果a>b成立,则c=a,否则c=b
print(c)

NAMESPACE 命名空间,名称空间

局部命名空间:定义函数都拥有自己的命名空间(局部命名空间可以有多个)。(函数内部定义的变量名,当调用函数的时候才会产生这个名字空间,随着函数的结束这个名字空间又消失)

##多个函数应该拥有多个局部的名字空间,且不互相共享。

 

def  func1():
    a = 1

def func2():
    print(a)

func2()
##
NameError: name 'a' is not defined

 

 

 

全局命名空间:写在函数外面的变量名和函数名(全局命名空间,是在程序从上到下执行的过程中依次加载进内存里的,放置了全局变量名和函数名)

内置命名空间:python解释器启动之后就可以使用的名字(Python解释器一启动就能认识的函数,这些函数就存放在内置命名空间里,这些函数(内置的名字)在解释器启动时加载到内存里

########################################################################################################################################

作用域:

全局作用域:-----作用在 全局-------内置和全局名字空间中的名字都属于全局作用域

局部作用域:-------作用在局部-------函数(局部名字空间中的名字)

 

#######################################################################################################################################

Python的解释器要运行起来

加载顺序:

先加载所有内置命名空间中的名字,然后按照顺序加载全局命名空间中的名字。

局部命名空间中的名字:在调用函数的时候产生,并且随着调用的结束而消失。

###1.在局部可以使用全局和内置命名空间中的名字

###2.在全局可以使用内置命名空间中的名字,但不能使用局部命名空间中的名字(当定义的函数执行完后局部空间就被释放了,消失了)

###3.在内置不能使用全局和局部的名字

(依赖倒置原则,上层模块只能依赖下层模块。反之则不可!)

##例:

例如:max()本身是个内置函数

print(max([1,2,3]))
#3

如果定义一个和内置函数相同名字的函数,则执行定义的函数(全局名字空间的函数,当自己有的时候就不找上级要,如果自己没有就找最近的上级要,如果在没有就找更上级要!)

def max(L):
    print("In max function!")

print(max([1,2,3]))
##
In max function! #执行的是定义的函数(虽然和内置函数同名)
None #由于没有返回值,所以打印的是None

 

作用域:一个名字可以使用的区域。局部作用域可以使用全局作用域中的变量,而全局作用域不能使用局部作用域中的变量;在局部作用域中还可以嵌套更小的局部作用域。

全局作用域:内置名字空间和全局名字空间中的名字属于全局作用域

局部作用域:局部名字空间中的名字属于局部作用域(对于不可变数据类型,在局部只能查看全局作用域的变量,但是不能修改,如果要修改要在变量前加global)

作用域链:小范围作用域可以使用大范围的变量,但作用域链是单向的,不能反向应用。

globals():保存了在全局作用域中的名字和值;

globals和locals方法:小范围可以使用大范围的,但是不能修改,如果想要修改,可以使用global关键字,但是要尽量避免使用,这是因为使用global之后会将全局变量修改掉,这样可能会使其他的要使用原来的全局变量的函数发生变化。(globals()永远打印全局,locals()输出什么要看它出现在什么位置)

locals():保存了当前作用域中的变量,其中的内容会根据执行的位置来决定作用域中的内容,如果是在全局执行,打印的结果会与globals打印的结果一致。

n=1
def func():
    global n  # 在加入这句话之前函数func中没有定义n,会报错。
    n += 1
func()
print(n) #如果在一个局部内声明了一个global变量,这个变量在全局有效

#2
PS:是要尽量避免使用,这是因为使用global之后会将全局变量修改掉,这样可能会使其他的要使用原来的全局变量的函数发生变化。

##locals()

a = 1
b = 2
def func():
    x = "aaa"
    y = "bbb"
    print(locals())  #使用locals()可以查看局部空间里的所有名字
func()
print(globals())  #使用globals()不仅能够查看全局名字空间里的名字,还可以查看内置名字空间里的名字
## {'y': 'bbb', 'x': 'aaa'}

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000224A85F8828>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Dell/PycharmProjects/s9/day10/函数进阶.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x00000224A84F2E18>}

##locals();globals()

a = 1
b = 2
def func():
    x = "aaa"
    y = "bbb"
func()
print(globals())
print(locals())#本地的
(输出的结果一样,如果locals()放在全局,打印的就是全局,如果放在局部就是局部)
###
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002A1B06F8828>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Dell/PycharmProjects/s9/day10/函数进阶.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x000002A1B06A2E18>}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000002A1B06F8828>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Dell/PycharmProjects/s9/day10/函数进阶.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x000002A1B06A2E18>}

## nonlcoal 变量名  修改最近的拥有该变量的外层函数的变量,不会影响全局。

n=0
def func():
    n=1
    def func2():
        nonlocal n  # 调用上一级函数中的n
        n+=1
    func2()
    print(n)  # n=2
func()
print(n)  # 使用全局变量中的n=0

# 2
# 0

 

posted @ 2018-11-20 15:08  shaopan  阅读(4434)  评论(0编辑  收藏  举报