zoukankan      html  css  js  c++  java
  • Python3 函数

    1.定义函数

      函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。

      任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。

      函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。

      函数内容以冒号起始,并且缩进。

      return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

      可以返回多个值,其实就是一个tuple。

    def my_abs(x):
        if x >= 0:
            return x
        else:
            return -x

      空函数:如果想定义一个什么事也不做的空函数,可以用来占位,可以用pass语句:

    def nop():
        pass

    2.参数传递 

      可更改(mutable)与不可更改(immutable)对象
      在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

      不可变类型:变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变a的值,相当于新生成了a。

      可变类型:变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

      python 函数的参数传递:

      不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。

      可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响

      python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

    3.参数

      调用函数时可使用的正式参数类型:必需参数,关键字参数,默认参数,不定长参数等,参数类型也可以组合。

      a. 必须参数,也叫位置参数

      必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

      b.关键字参数

      关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。

      使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

    def printinfo( name, age ):
       "打印任何传入的字符串"
       print ("名字: ", name)
       print ("年龄: ", age)
       return
     
    #调用printinfo函数
    printinfo( age=50, name="runoob" )

      c.默认参数

      调用函数时,如果没有传递参数,则会使用默认参数。默认参数要放在最后。默认参数必须指向不可变对象。

    def printinfo( name, age = 35 ):
       "打印任何传入的字符串"
       print ("名字: ", name)
       print ("年龄: ", age)
       return
     
    #调用printinfo函数
    printinfo( age=50, name="runoob" )
    print ("------------------------")
    printinfo( name="runoob" )

      d.不定长参数,可变参数,收集参数

      需要一个函数能处理比当初声明时更多的参数。

      加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。

      果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。

      如果* 后,还有参数,参数必须用关键字传入。

      加了两个星号 ** 的参数会以字典的形式导入。允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

    def printinfo( arg1, *vartuple ):
       "打印任何传入的参数"
       print ("输出: ")
       print (arg1)
       for var in vartuple:
          print (var)
       return
     
    # 调用printinfo 函数
    printinfo( 10 )
    printinfo( 70, 60, 50 )

    >>> nums = [1, 2, 3]
    >>> calc(*nums)
    14

    def fun(**kwargs):
    for x, y in kwargs.items():
    print(x,y)

    fun(city = "asdasd",beijing = "sadasdw")

    4.匿名函数

      python 使用 lambda 来创建匿名函数。

      所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

      lambda 只是一个表达式,函数体比 def 简单很多。
      lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
      lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
      虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

    sum = lambda arg1, arg2: arg1 + arg2
    

      请参考:https://www.cnblogs.com/kaishirenshi/p/8611358.html

      two_sum = (lambda x, y: x + y)(34)

      匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:

    >> f = lambda x: x * x
    >>> f
    <function <lambda> at 0x101c6ef28>
    >>> f(5)
    25

       也可以把匿名函数作为返回值返回,

    def build(x, y):
        return lambda: x * x + y * y

    5.高阶函数

      一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

    def add(x, y, f):
        return f(x) + f(y)
    
    def zheng(n):
        if n > 0:
            return n
        else:
            return -n
    
    x = -5
    y = 10
    f = zheng  #变量指向函数,函数名就是变量
    print(add(x, y, f))

    5.1 map()reduce()函数

    map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

    >>> def f(x):
    ...     return x * x
    ...
    >>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
    >>> list(r)
    [1, 4, 9, 16, 25, 36, 49, 64, 81]

    再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算:

    reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
    >>> from functools import reduce
    >>> def add(x, y):
    ...     return x + y
    ...
    >>> reduce(add, [1, 3, 5, 7, 9])
    25

      作业:

      利用mapreduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456

      摘选了一个答案:

    def str2float(str):
    
        d = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
             '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    
        def s2n(s):
            return d[s]
    
        def fn(a, b):
            return reduce(lambda x, y: x * 10 + y, map(s2n, a)) + reduce(lambda x, y: x * 10 + y, map(s2n, b)) * (10 ** -len(b))
    
        theS = str.split('.')
    
        return fn(theS[0], theS[1])

     5.2 filter

    Python内建的filter()函数用于过滤序列。

    map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

    def is_odd(n):
        return n % 2 == 1
    
    list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
    # 结果: [1, 5, 9, 15]

    用filter求素数

    def _odd_iter():  #构造一个从3开始的奇数序列,这是一个生成器,并且是一个无限序列。
        n = 1
        while True:
            n = n + 2
            yield n
    def _not_divisible(n): #定义一个筛选函数 return lambda x: x % n > 0
    def primes(): #定义一个生成器,不断返回下一个素数 yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一个数 #每次循环,it会改变,n取新序列的第一个数。 yield n it = filter(_not_divisible(n), it) # 构造新序列 #循环一次,就将n及n倍数的值去掉返回新的Iterator
    #将lambda函数写在filter中,筛选有问题,没搞懂是怎么回事,应该是闭包的问题。
    # 打印1000以内的素数: for n in primes(): if n < 1000: print(n) else: break

    5.3 stored

    sorted()函数也是一个高阶函数,可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

     先将序列输入key函数,获取新序列,然后再排序。

    >>> sorted([36, 5, -12, 9, -21])
    [-21, -12, 5, 9, 36]
    
    >>> sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]
    
    >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)  #反向排序
    ['Zoo', 'Credit', 'bob', 'about']

    key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。

    序列中的元素是一个个传入key函数,再返回一个新序列。

    max,min

    salaries={
        'siry':3000,
        'tom':7000,
        'lili':10000,
        'jack':2000
    }
    # 需求1:找出薪资最高的那个人=》lili
    # res=max([3,200,11,300,399])
    # print(res)
    
    # res=max(salaries)
    # print(res)
    
    
    salaries={
        'siry':3000,
        'tom':7000,
        'lili':10000,
        'jack':2000
    }
    # 迭代出的内容    比较的值
    # 'siry'         3000
    # 'tom'          7000
    # 'lili'         10000
    # 'jack'         2000
    
    # def func(k):
    #     return salaries[k]
    
    # ========================max的应用
    # res=max(salaries,key=func) # 返回值=func('siry')
    # print(res)
    
    # res=max(salaries,key=lambda k:salaries[k])
    # print(res)
    
    # ========================min的应用
    # res=min(salaries,key=lambda k:salaries[k])
    # print(res)

    5.4 函数作为返回值

      闭包

      https://blog.csdn.net/qq_37616069/article/details/79646905

      在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包

      函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包

      #形成闭包的条件
        #1、必须要有一个内嵌函数
        #2、内嵌函数中要对外层函数变量的引用
        #3、外部函数必须返回内嵌函数

     返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

    如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

    def count():
        def f(j):
            def g():
                return j*j
            return g
        fs = []
        for i in range(1, 4):
            fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
        return fs

     5.5 装饰器

      可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

      要先理解闭包。

        def outer(func):  # 装饰函数
            def inner():
                print("认证成功!")
                result = func()
                print("日志添加成功")
                return result
            return inner
    
        @outer
        def f1():  # 被装饰函数
            print("业务部门1数据接口......")

      a.程序开始运行,从上到下开始解释。读到def outer(func)时,发现这是一个函数的定义,将其函数体放入内存中,然后跳过。

      b.跳到@outer时,程序被@这个python语法糖吸引住,知道这是个装饰器,按规矩要立即执行,于是程序开始运行@后面那个名字outer所定义的函数。

      c.程序返回到outer函数,开始执行装饰器的语法规则。规则是:被装饰函数的名字会被当作函数形参传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰函数。原来f1函数被当作参数传递给了func,而f1这个函数名之后会指向inner函数。 注意:@outer和outer()是有区别的。没有括号时,outer函数会被立即执行。这个与传统的用括号才能调用函数不同。

      d.程序开始执行outer函数内部的内容,一开始它又碰到了一个函数inner,inner函数定义块被程序观察到后不会立刻执行,而是读入内存中(这是默认规则)。

      e.再往下,碰到return inner,返回值是个函数名,并且这个函数名会被赋值给f1这个被装饰函数,也就是f1 = inner。此时,f1函数被新的函数inner覆盖了(实际上是f1这个函数名更改成指向inner这个函数名指向的函数体内存地址,f1不再指向它原来的函数体的内存地址),再往后调用f1的时候将执行inner函数内的代码,而不是先前的函数体。那么先前的函数体去哪了?还记得我们将f1当做参数传递给func这个形参么?func这个变量保存了老的函数在内存中的地址,通过它就可以执行老的函数体,你能在inner函数里看到result = func()这句代码,它就是这么干的!

      f.接下来,还没有结束。当业务部门,依然通过f1()的方式调用f1函数时,执行的就不再是旧的f1函数的代码,而是inner函数的代码。在本例中,它首先会打印个“认证成功”的提示,很显然你可以换成任意的代码,这只是个示例;然后,它会执行func函数并将返回值赋值给变量result,这个func函数就是旧的f1函数;接着,它又打印了“日志添加成功”的提示,这也只是个示例,可以换成任何你想要的;最后返回result这个变量。我们在业务部门的代码上可以用r = f1()的方式接收result的值。

      g.以上流程走完后,你应该看出来了,在没有对业务部门的代码和接口调用方式做任何修改的同时,也没有对基础平台部原有的代码做内部修改,仅仅是添加了一个装饰函数,就实现了我们的需求,在函数调用前进行认证,调用后写入日志。这就是装饰器的最大作用。

      f1._name_    输出inner。Python内置functools.wraps(),可以解决它们的__name__已经从原来的'f1'变成了'inner'的问题

    import functools
    
    def log(text):    ###now = log('execute')(now)
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator
    @log('execute') ###decorator本身需要传入参数
    def now():
        print('2015-3-25')
    #带参数
    def outer(func):
        def inner(*args, *kwargs):
            print("认证成功")
            result = func(*args, **kwargs)
            print("日志添加成功")
            return result
        return inner
    @outer
    def f1(name, age):
        print("{}正在调用业务部门1的数据接口".format(name))
    # 调用方法
    f1("jack", 19)
    #有多个装饰器
    def outer1(func):
        def inner(*args,**kwargs):
            print("认证成功!")
            result = func(*args,**kwargs)
            print("日志添加成功")
            return result
        return inner
    
    
    def outer2(func):
        def inner(*args,**kwargs):
            print("一条欢迎信息。。。")
            result = func(*args,**kwargs)
            print("一条欢送信息。。。")
            return result
        return inner
    
    
    @outer1
    @outer2
    def f1(name,age):
        print("%s 正在连接业务部门1数据接口......"%name)
    
    # 调用方法
    f1("jack",18)
    ###装饰器中形参是函数   
     # 认证函数
        def  auth(request,kargs):
            print("认证成功!")
            
        # 日志函数
        def log(request,kargs):
            print("日志添加成功")
            
        # 装饰器函数。接收两个参数,这两个参数应该是某个函数的名字。
        def Filter(auth_func,log_func):
            # 第一层封装,f1函数实际上被传递给了main_fuc这个参数
            def outer(main_func):
                # 第二层封装,auth和log函数的参数值被传递到了这里
                def wrapper(request,kargs):
                    # 下面代码的判断逻辑不重要,重要的是参数的引用和返回值
                    before_result = auth(request,kargs)
                    if(before_result != None):
                        return before_result;
        
                    main_result = main_func(request,kargs)
                    if(main_result != None):
                        return main_result;
        
                    after_result = log(request,kargs)
                    if(after_result != None):
                        return after_result;
                return wrapper
            return outer
        # 注意了,这里的装饰器函数有参数哦,它的意思是先执行filter函数。然后将filter函数的返回值作为装饰器函数的名字返回到这里。所以,Filter(auth,log) = outer , @Filter(auth,log) =  @outer
        @Filter(auth,log)
        def f1(name,age):
            print("%s 正在连接业务部门1数据接口......"%name)
        
        # 调用方法
        f1("jack",18)

     5.6 偏函数

      functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数。

    >>> import functools
    >>> int2 = functools.partial(int, base=2)
    >>> int2('1000000')
    64
    >>> int2('1010101')
    85

    固定参数,返回新的函数。

     参考:菜鸟教程和廖雪峰Python3

     https://www.jianshu.com/p/affeb6acf1c9

  • 相关阅读:
    变色龙
    生在北极圈内的“瑞典之声”Sofia Jannok
    “清一色”数列1,11,111,1111,...
    1100 10100 PAGE ERROR
    The Lazarus Project
    数字“黑洞”495
    don't you forget the EXIF~!
    行和列
    小谈奇数平方与偶数平方
    “码农”的得力工具math
  • 原文地址:https://www.cnblogs.com/yunlong-study/p/12555954.html
Copyright © 2011-2022 走看看