python基础教程(第三版)学习笔记(六)

第六章 抽象(函数)
6.1 懒惰是一种美德
6.2 抽象和结构
6.3 自定义函数
判断某个对象是否可调用,可使用内置函数callable。格式是callable(对象)。
函数是结构化编程的核心。使用def(表示定义函数)语句。
'''
 

def fun(str_s):  #定义函数
    return str_s #函数返回内容
f=fun("你好")     #调用函数
print(f)

'''

你好


------------------
(program exited with code: 0)

请按任意键继续. . .


6.3.1 给函数编写文档要给函数编写文档,以确保其他人能够理解,可添加注释(以#打头的内容)。还有另一种编写注释的方式,就是添加独立的字符串。在有些地方,如def语句后面(以及模块和类的开头),添加这样的字符串很有用。放在函数开头的字符串称为文档字符串(docstring),将作为函数的一部分存储起来。
'''
 

def fun(a):
    "这是一个简单的输入字符的函数"
    return a*a
helps=fun.__doc__
print(helps)

'''

这是一个简单的输入字符的函数


------------------
(program exited with code: 0)

请按任意键继续. . .


也可以用三重双(单)引号定义函数文档。
'''
 

def fun_(str_s):
    '''
    "这是一个简单的函数"
    '''
    return str_s
fu=fun_("你好吗?")
fuc=fun_.__doc__
print (fu,"\n",fuc)

'''

这是一个简单的输入字符的函数
你好吗?

        "这是一个简单的函数"


------------------
(program exited with code: 0)

请按任意键继续. . .

特殊的内置函数help很有用。在交互式解释器中,可使用它获取有关函数的信息,其中包含函数的文档字符串。
'''

print(help(fun_))


'''

'more' 不是内部或外部命令,也不是可运行的程序
或批处理文件。


------------------
(program exited with code: 0)

请按任意键继续. . .


注:如果出现这种情况,把C:/windows/system32添加到系统环境变量的path中。
6.3.2 其实函数并不是数学意义上的函数
数学意义上的函数始终有确定的返回值,而这里的函数即使有return语句也可能相当于break只是结束函数,而break是结束循环而已。

'''
 

def function_():
    print("你好")
    return
    print("很好!")
function_()

'''

你好
None


------------------
(program exited with code: 0)

请按任意键继续. . .


这是一个熟悉的值:None。由此可知,所有的函数都返回值。如果你没有告诉它们该返回什么,将返回None。

6.4 参数魔法
6.4.1 值从哪里来
编写函数旨在为当前程序(甚至其他程序)提供服务,确保它在提供的参数正确时完成任务,并在参数不对时以显而易见的方式失败。
6.4.2 参数能修改吗?
定义函数是的参数名和要传递的参数名是两码事。定义的函数参数叫形参,要传递的参数叫实参。实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。在函数内部重新关联参数(即给它赋值)时,函数外部的变量不受影响。
6.4.3 关键字参数和默认值
没有默认值的参数是位置参数。
'''
 

def f1(n,m):
    print("{},{}".format(n,m))
    
def f2(m,n):
    print("{},{}".format(m,n))
    
print(f1("你","好"))
print(f2("你","好"))

'''

你,好
None
你,好
None


------------------
(program exited with code: 0)

请按任意键继续. . .


参数的排列顺序可能难以记住,尤其是参数很多时。为了简化调用工作,可指定参数的名称。
'''
 

def score(name="王五",va=80):
    print("{}的成绩是:{}分".format(name,va))
print(score())

'''

王五的成绩是:80分
None


------------------
(program exited with code: 0)

请按任意键继续. . .

其中王五是name的默认值,80是va的默认值,而这种由默认值的参数。
'''
 

print(score("李四"))

'''

李四的成绩是:80分
None


------------------
(program exited with code: 0)

请按任意键继续. . .


'''
 

print(score("李四",90))

'''

李四的成绩是:90分
None


------------------
(program exited with code: 0)

请按任意键继续. . .


'''
 

print(score(va=90))

'''

王五的成绩是:90分
None


------------------
(program exited with code: 0)

请按任意键继续. . .


注意:如果python的函数可以用=直接给参数赋值。如果第一个关键字参数不赋值在python中用,号区隔会出现错误,只能用=号直接赋值。
6.4.4 收集参数
在形参前加*号可以允许输入任意多个实参。
'''
 

def number(*n):
    print(n)
number(1,2,3,4,5,6)

'''

(1, 2, 3, 4, 5, 6)


------------------
(program exited with code: 0)

请按任意键继续. . .


参数前面的星号将提供的所有值都放在一个元组中,也就是将这些值收集起来。这样的行为我们在5.2.1节见过:赋值时带星号的变量收集多余的值。它收集的是列表而不是元组中多余的值,但除此之外,这两种用法很像。
'''
 

def dic(**d):
    return d
print(dic())

'''

{}


------------------
(program exited with code: 0)

请按任意键继续. . .


参数前面的双星号将提供的所有值都放在一个字典中,这种函数的实参要求同时传入参数名称和值。
'''

print(dic(name="许九",age=50))


'''

{'name': '许九', 'age': 50}


------------------
(program exited with code: 0)

请按任意键继续. . .


这种参数叫关键字参数,(有些书籍把带有默认值参数的位置参数也叫做关键字参数)。
6.4.5 分配参数
在调用函数时的实参前面加*号。
'''

def sum(x,y):
    return (x+y)
s=[1,4]
print(sum(*s))

'''

5


------------------
(program exited with code: 0)

请按任意键继续. . .


'''

d={"ID":2018,"name":"张三","age":60}
print(dic(**d))


'''

{'ID': 2018, 'name': '张三', 'age': 60}


------------------
(program exited with code: 0)

请按任意键继续. . .


在python里还有一种参数,限制关键字参数,它要求实参不但要有名称和值而且关键字是指定的。
'''

def f(*,name,age):
    return name+"的年龄是"+str(age)+"岁。"
    
print(f(name="王五",age=25))


'''

王五的年龄是25岁。


------------------
(program exited with code: 0)

请按任意键继续. . .


PEP 8(https://www.python.org/dev/peps/pep-0008/)建议代码行的长度不要超过79字符,这样只要编辑器窗口适中,就能看到整行代码。如果形参很多,导致函数定义的长度超过了79字符,可在函数定义中输入左括号后按回车键,并在下一行按两次Tab键,从而将形参列表和只缩进一层的函数体区分开来。
'''
 

def fnn(
    
        numb1,numb2,
        numb3,numb4,
        numb5,numb6):
            """
这是一个空函数,用于测试多参数函数的书写方法,是否正确。
            """
            pass
fnn(1,2,3,4,5,6)
help(fnn)

'''

Help on function fnn in module __main__:

fnn(numb1, numb2, numb3, numb4, numb5, numb6)
    这是一个空函数,用于测试多参数函数的书写方法,是否正确。


------------------
(program exited with code: 0)

请按任意键继续. . .


6.5 作用域
变量的有效范围就是变量的作用域。在python中分为全局作用域和函数作用域。在函数外部定义的变量的作用域较全局作用域;在函数内部定义的变量叫函数作用域,又叫局部作用域。python中没有块作用域。
'''
 

v=5
if v!=5:   #全局变量
    a=2
    pass       
def fn():
    fnv=3  #函数变量
    print(v,fnv)
    print(locals())
print(v)
fn()
print(locals())

'''

5
5 3
{'fnv': 3}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_f
rozen_importlib_external.SourceFileLoader object at 0x0000000001D6DA58>, '__spec
__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>
, '__file__': 'xx.py', '__cached__': None, 'v': 5, 'fn': <function fn at 0x000
0000001D2C1E0>}


------------------
(program exited with code: 0)

请按任意键继续. . .


由此可以看出,fn中的变量只有一个fnv,在全局中的全局变量是v。而locals所指示的是变量的存储空间,又叫命名空间,它是一个字典。作用域和命名空间有着密切关系,但由此可以看出,命名空间和作用域是有区别的,作用域是指的有效范围,而命名空间是指地所在的字典(空间),一个对象的作用域只有一个,而命名空间不是,对于变量,每调用一次变量所在的函数一次,就会产生一个命名空间,函数结束,这个空间瞬即消亡。
读取全局变量的值通常不会有问题,但还是存在出现问题的可能性。如果有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量遮住了。
如果需要,可使用函数globals来访问全局变量。这个函数类似于vars,返回一个包含全局变量的字典。
'''
 

w=10
def fn1():
    w=7
    print("函数外部变量w",globals()['w'])
    print("函数内部变量w",w)
fn1()

'''

函数外部变量w 10
函数内部变量w 7


------------------
(program exited with code: 0)

请按任意键继续. . .


在函数中为全局变量赋值,用global明示该变量为全局变量。
'''
 

w=10
def fn1():
    global w
    w=7
    print("函数外部变量w",globals()['w'])
    print("函数内部变量w",w)
fn1()

'''

函数外部变量w 7
函数内部变量w 7


------------------
(program exited with code: 0)

请按任意键继续. . .


另外,还有一个关键字nonlocal用它可以在内部函数内给外部函数的变量赋值。正像用global给全局变量赋值那样。

6.6 递归
递归函数就是函数调用自身并返回。
6.6.1 两个经典案例:阶乘和幂
阶乘:n*(n-1)*(n-2)...*2*1
'''
 

def fact(n):                #n的阶乘
    if n==1:                #如果n=1
        return 1            #则返回1
    else:                   #否则
        return n*fact(n-1)  #n*(n-1)
print(fact(10))

'''

3628800


------------------
(program exited with code: 0)

请按任意键继续. . .


幂:x^y即y个x相乘。
'''
 

def powe(x,y):                #y个x相乘
    if y==0:                  #如果y等于零
        return 1              #就返回一,
    else:                     #否则
        return x*powe(x,y-1)  #x乘于(y-1)个x
p=powe(2,3)
print(p)

'''

8


------------------
(program exited with code: 0)

请按任意键继续. . .


6.6.2 另一个经典案例:二分查找
如果上限和下限相同,就说明它们都指向数字所在的位置,因此将这个数字返回。
否则,找出区间的中间位置(上限和下限的平均值),再确定数字在左半部分还是右半部分。然后在继续在数字所在的那部分中查找。

def search(sequence, number, lower, upper):  #sequence序列、number数、lower下限、upper上限。
    if lower == upper:                       #如果上下限相等(序列仅一个数)
        assert number == sequence[upper]     #设置断言:如果查找的数等于这个数,
        return upper                         #就返回这个位置。
    else:                                    #否则
        middle = (lower + upper) // 2        #取中间位子
        if number > sequence[middle]:        #如果查找的数大于中间位置的数
            return search(sequence, number, middle + 1, upper) #就把中间位置作为下限查找,
        else:                                                  #否则
            return search(sequence, number, lower, middle)     #就把中间位置作为上限查找。


为方便调用,还可将上限和下限设置为可选的。为此,只需给参数lower和upper指定默认值,并在函数开头添加如下条件语句:

def search(sequence, number, lower=0, upper=None):  
    if upper is None: upper = len(sequence) - 1


'''
 

def search(sequence, number, lower=0, upper=None):  
    if upper is None: upper = len(sequence) - 1
    if lower == upper:                       
        assert number == sequence[upper]    
        return upper                         
    else:                                   
        middle = (lower + upper) // 2       
        if number > sequence[middle]:        
            return search(sequence, number, middle + 1, upper)
        else:                                                 
            return search(sequence, number, lower, middle)
            
seq=[34,67,8,123,4,100,95]
seq.sort()
print(seq)
sea=search(seq,34)
print(sea)

'''

[4, 8, 34, 67, 95, 100, 123]
2


------------------
(program exited with code: 0)

请按任意键继续. . .


实际上,模块bisect提供了标准的二分查找实现,可以参阅。
附:函数式编程——介绍几个函数
1、map
map() 会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
格式为:map(function, iterable, ...)Python 2.x 返回列表;Python 3.x 返回迭代器。
其中function 为函数,iterable为一个或多个序列
'''
 

x=[1,2,3,4,5]
y=[9,8,7,6,5]
def lis(x,y):
    return x+y
m1=map(lis,x,y)
print(list(m1))

'''

[10, 10, 10, 10, 10]


------------------
(program exited with code: 0)

请按任意键继续. . .


也可以这样写:
'''
 

m2=map(lambda i,j:i+j ,x,y)
print(list(m2))

'''

[10, 10, 10, 10, 10]


------------------
(program exited with code: 0)

请按任意键继续. . .


其中lambda是用来定义匿名函数的表达式。格式为:lambda 参数1,参数2...:函数体
2、filter
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
格式为filter(function, iterable)
其中function为判断函数,iterable为可迭代对象。
'''
 

nums=[2,"d","&&&","a3","..."]
fi=filter(lambda x:str(x).isalnum(),nums)
print(list(fi))

'''

[2, 'd', 'a3']


------------------
(program exited with code: 0)

请按任意键继续. . .


其中isalnum() 方法检测字符串是否由字母和数字组成。如果字符串中至少有一个字符并且所有字符都是字母或数字则返回 True,否则返回 False
3、 reduce
reduce() 函数会对参数序列中元素进行累积。
格式为reduce(function, iterable[, initializer])
其中function为函数,有两个参数;iterable为可迭代对象;initializer为可选,初始参数。
其功能是将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
'''
 

arr=[1,2,3,4,5,6,7,8,9,10]      #这里不能用arr=range(10),不然结果为零。
from functools import reduce
ss=reduce(lambda x,y:x*y,arr)   #10的阶乘
print(ss)

'''

3628800


------------------
(program exited with code: 0)

请按任意键继续. . .

(待续)

猜你喜欢

转载自blog.csdn.net/micorjun/article/details/83580799