zoukankan      html  css  js  c++  java
  • python中定义函数和参数的传递问题

    作者:達聞西
    链接:https://zhuanlan.zhihu.com/p/24162430
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    5.2.4 函数、生成器和类

    还是从几个例子看起:

    def say_hello():
        print('Hello!')
    
    def greetings(x='Good morning!'):
        print(x)
    
    say_hello()                 		# Hello!
    greetings()                 		# Good morning!
    greetings("What's up!")     		# What's up!
    a = greetings()             		# 返回值是None
    
    def create_a_list(x, y=2, z=3):	# 默认参数项必须放后面
        return [x, y, z]
    
    b = create_a_list(1)        		# [1, 2, 3]
    c = create_a_list(3, 3)     		# [3, 2, 3]
    d = create_a_list(6, 7, 8)  	# [6, 7, 8]
    
    def traverse_args(*args):
        for arg in args:
            print(arg)
    
    traverse_args(1, 2, 3)              # 依次打印1, 2, 3
    traverse_args('A', 'B', 'C', 'D')   # 依次打印A, B, C, D
    
    def traverse_kargs(**kwargs):
        for k, v in kwargs.items():
            print(k, v)
    
    traverse_kargs(x=3, y=4, z=5)       # 依次打印('x', 3), ('y', 4), ('z', 5)
    traverse_kargs(fighter1='Fedor', fighter2='Randleman')
    
    def foo(x, y, *args, **kwargs):
        print(x, y)
        print(args)
        print(kwargs)
    
    # 第一个pring输出(1, 2)
    # 第二个print输出(3, 4, 5)
    # 第三个print输出{'a': 3, 'b': 'bar'}
    foo(1, 2, 3, 4, 5, a=6, b='bar')
    

    其实和很多语言差不多,括号里面定义参数,参数可以有默认值,且默认值不能在无默认值参数之前。Python中的返回值用return定义,如果没有定义返回值,默认返回值是None。参数的定义可以非常灵活,可以有定义好的固定参数,也可以有可变长的参数(args: arguments)和关键字参数(kargs: keyword arguments)。如果要把这些参数都混用,则固定参数在最前,关键字参数在最后。

    Python中万物皆对象,所以一些情况下函数也可以当成一个变量似的使用。比如前面小节中提到的用字典代替switch-case的用法,有的时候我们要执行的不是通过条件判断得到对应的变量,而是执行某个动作,比如有个小机器人在坐标(0, 0)处,我们用不同的动作控制小机器人移动:

    moves = ['up', 'left', 'down', 'right']
    
    coord = [0, 0]
    
    for move in moves:
        if move == 'up':        # 向上,纵坐标+1
            coord[1] += 1
        elif move == 'down':    # 向下,纵坐标-1
            coord[1] -= 1
        elif move == 'left':    # 向左,横坐标-1
            coord[0] -= 1
        elif move == 'right':   # 向右,横坐标+1
            coord[0] += 1
        else:
            pass
        print(coord)            # 打印当前位置坐标
    

    不同条件下对应的是对坐标这个列表中的值的操作,单纯的从字典取值就办不到了,所以就把函数作为字典的值,然后用这个得到的值执行相应动作:

    moves = ['up', 'left', 'down', 'right']
    
    def move_up(x):         # 定义向上的操作
        x[1] += 1
    
    def move_down(x):       # 定义向下的操作
        x[1] -= 1
    
    def move_left(x):       # 定义向左的操作
        x[0] -= 1
    
    def move_right(x):      # 定义向右的操作
        x[0] += 1
    
    # 动作和执行的函数关联起来,函数作为键对应的值
    actions = {
        'up': move_up,
        'down': move_down,
        'left': move_left,
        'right': move_right
    }
    
    coord = [0, 0]
    
    for move in moves:
        actions[move](coord)
        print(coord)
    

    把函数作为值取到后,直接加一括号就能使了,这样做之后起码在循环部分看上去很简洁。有点C里边函数指针的意思,只不过更简单。其实这种用法在之前讲排序的时候我们已经见过了,就是operator中的itemgetter。itemgetter(1)得到的是一个可调用对象(callable object),和返回下标为1的元素的函数用起来是一样的:

    def get_val_at_pos_1(x):
        return x[1]
    
    heros = [
        ('Superman', 99),
        ('Batman', 100),
        ('Joker', 85)
    ]
    
    sorted_pairs0 = sorted(heros, key=get_val_at_pos_1)
    sorted_pairs1 = sorted(heros, key=lambda x: x[1])
    
    print(sorted_pairs0)
    print(sorted_pairs1)
    

    在这个例子中我们用到了一种特殊的函数:lambda表达式。Lambda表达式在Python中是一种匿名函数,lambda关键字后面跟输入参数,然后冒号后面是返回值(的表达式),比如上边例子中就是一个取下标1元素的函数。当然,还是那句话,万物皆对象,给lambda表达式取名字也是一点问题没有的:

    some_ops = lambda x, y: x + y + x*y + x**y
    some_ops(2, 3)  # 2 + 3 + 2*3 + 2^3 = 19
    

    生成器(Generator

    生成器是迭代器的一种,形式上看和函数很像,只是把return换成了yield,在每次调用的时候,都会执行到yield并返回值,同时将当前状态保存,等待下次执行到yield再继续:

    # 从10倒数到0
    def countdown(x):
        while x >= 0:
            yield x
            x -= 1
    
    for i in countdown(10):
        print(i)
    
    # 打印小于100的斐波那契数
    def fibonacci(n):
        a = 0
        b = 1
        while b < n:
            yield b
            a, b = b, a + b
    
    for x in fibonacci(100):
        print(x)
    

    生成器和所有可迭代结构一样,可以通过next()函数返回下一个值,如果迭代结束了则抛出StopIteration异常:

    a = fibonacci(3)
    print(next(a))  # 1
    print(next(a))  # 1
    print(next(a))  # 2
    print(next(a))  # 抛出StopIteration异常
    

    Python3.3以上可以允许yield和return同时使用,return的是异常的说明信息:

    # Python3.3以上可以return返回异常的说明
    def another_fibonacci(n):
        a = 0
        b = 1
        while b < n:
            yield b
            a, b = b, a + b
        return "No more ..."
    
    a = another_fibonacci(3)
    print(next(a))  # 1
    print(next(a))  # 1
    print(next(a))  # 2
    print(next(a))  # 抛出StopIteration异常并打印No more消息
    

    类(Class

    Python中的类的概念和其他语言相比没什么不同,比较特殊的是protected和private在Python中是没有明确限制的,一个惯例是用单下划线开头的表示protected,用双下划线开头的表示private:

    class A:
        """Class A"""
        def __init__(self, x, y, name):
            self.x = x
            self.y = y
            self._name = name
    
        def introduce(self):
            print(self._name)
    
        def greeting(self):
            print("What's up!")
    
        def __l2norm(self):
            return self.x**2 + self.y**2
    
        def cal_l2norm(self):
            return self.__l2norm()
    
    a = A(11, 11, 'Leonardo')
    print(A.__doc__)        	# "Class A"
    a.introduce()           	# "Leonardo"
    a.greeting()            	# "What's up!"
    print(a._name)          	# 可以正常访问
    print(a.cal_l2norm())   # 输出11*11+11*11=242
    print(a._A__l2norm())   # 仍然可以访问,只是名字不一样
    print(a.__l2norm())     	# 报错: 'A' object has no attribute '__l2norm'
    

    类的初始化使用的是__init__(self,),所有成员变量都是self的,所以以self.开头。可以看到,单下划线开头的变量是可以直接访问的,而双下划线开头的变量则触发了Python中一种叫做name mangling的机制,其实就是名字变了下,仍然可以通过前边加上“_类名”的方式访问。也就是说Python中变量的访问权限都是靠自觉的。类定义中紧跟着类名字下一行的字符串叫做docstring,可以写一些用于描述类的介绍,如果有定义则通过“类名.__doc__”访问。这种前后都加双下划线访问的是特殊的变量/方法,除了__doc__和__init__还有很多,这里就不展开讲了。

    Python中的继承也非常简单,最基本的继承方式就是定义类的时候把父类往括号里一放就行了:

    class B(A):
        """Class B inheritenced from A"""
        def greeting(self):
            print("How's going!")
    
    b = B(12, 12, 'Flaubert')
    b.introduce()   # Flaubert
    b.greeting()    # How's going!
    print(b._name())        # Flaubert
    print(b._A__l2norm())   # “私有”方法,必须通过_A__l2norm访问
    

    5.2.5 map, reduce和filter

    map可以用于对可遍历结构的每个元素执行同样的操作,批量操作:

    map(lambda x: x**2, [1, 2, 3, 4])                 # [1, 4, 9, 16]
    
    map(lambda x, y: x + y, [1, 2, 3], [5, 6, 7])   # [6, 8, 10]
    

    reduce则是对可遍历结构的元素按顺序进行两个输入参数的操作,并且每次的结果保存作为下次操作的第一个输入参数,还没有遍历的元素作为第二个输入参数。这样的结果就是把一串可遍历的值,减少(reduce)成一个对象:

    reduce(lambda x, y: x + y, [1, 2, 3, 4])    # ((1+2)+3)+4=10
    

    filter顾名思义,根据条件对可遍历结构进行筛选:

    filter(lambda x: x % 2, [1, 2, 3, 4, 5])    # 筛选奇数,[1, 3, 5]
    

    需要注意的是,对于filter和map,在Python2中返回结果是列表,Python3中是生成器。


    5.2.6 列表生成(list comprehension)

    列表生成是Python2.0中加入的一种语法,可以非常方便地用来生成列表和迭代器,比如上节中map的两个例子和filter的一个例子可以用列表生成重写为:

    [x**2 for x in [1, 2, 3, 4]]                      # [1, 4, 9 16]
    
    [sum(x) for x in zip([1, 2, 3], [5, 6, 7])] # [6, 8, 10]
    
    [x for x in [1, 2, 3, 4, 5] if x % 2]       # [1, 3, 5]
    

    zip()函数可以把多个列表关联起来,这个例子中,通过zip()可以按顺序同时输出两个列表对应位置的元素对。如果要生成迭代器只需要把方括号换成括号,生成字典也非常容易:

    iter_odd = (x for x in [1, 2, 3, 4, 5] if x % 2)
    
    print(type(iter_odd))                       # <type 'generator'>
    
    square_dict = {x: x**2 for x in range(5)}   # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
    

    至于列表生成和map/filter应该优先用哪种,这个问题很难回答,不过Python创始人Guido似乎不喜欢map/filter/reduce,他曾在表示过一些从函数式编程里拿来的特性是个错误。

  • 相关阅读:
    express4.x socket
    validator
    服务管理,Dll查看
    复制程序,获取系统信息
    TCP HelloWord
    UDP HelloWord
    [置顶] 一个小马
    注册表编辑
    服务的启动与停止
    自下载运行
  • 原文地址:https://www.cnblogs.com/xiaoxiaoshuaishuai0219/p/6163763.html
Copyright © 2011-2022 走看看