精华内容
下载资源
问答
  • Python之函数变量

    2016-12-21 15:22:00
    Python之函数变量 本节内容 函数介绍及其作用 函数的定义与调用 函数的参数说明 变量与作用域 值传递和引用传递 一、函数的介绍及其作用 编程语言中的函数与数学中的函数是有区别的:数学中的...

    Python之函数与变量

    本节内容


    1. 函数介绍及其作用
    2. 函数的定义与调用
    3. 函数的参数说明
    4. 变量与作用域
    5. 值传递和引用传递

    一、函数的介绍及其作用


    编程语言中的函数与数学中的函数是有区别的:数学中的函数有参数(输入),就会有相应的结果(输出)。编程语言中的函数有输入,不一定会返回结果。编程语言中的函数其实就是一个用于完成某个特定功能的相关代码的代码段 。那么哪些代码语句应该被整合到一起定义为一个函数呢?这取决于你想让这个函数完成的功能是什么。

    为什么要将这个代码段定义成一个函数呢?这其实就是函数的作用。假设我们在编写一个可供用户选择的菜单程序,程序启动时需要打印一遍菜单列表,而且程序运行过程中用户也可以随时打印菜单列表,也就是说打印菜单列表的代码段可能要多次被用到,假设每次打印的菜单列表都是一样的,而且列表很长,那么我们是否应该每次在需要打印菜单的时候重复执行相同的代码呢?那么当我们需要增加或者减少一个菜单项时怎么办呢?显然我们需要在每个打印菜单的代码点都进行修改。如果我们把打印菜单的相关代码拿出来定义为一个函数,又会出现怎样的场景呢?我们只需要在需要打印菜单列表的地方使用这个函数;当需要添加或减少一个菜单项时,只需要修改这个函数中的内容即可,程序的维护和扩展成本大大降低;同时,我们这个程序的代码会更加简洁,而且有条理性更加便于阅读,而不是一坨乱糟糟的让人看着就想重写的东西。当然,如果你要打印的是多级菜单,你可以通过函数的参数或全局变量通知该函数要打印的是几级菜单。总结一下,编程语言中的函数的作用就是实现代码的可重用性,提高代码可维护性、扩展性和可读性

    二、函数的定义与调用


    1. 函数的定义

    高级编程语言通常会提供很多内置的函数来屏蔽底层差异,向上暴露一些通用的接口,比如我们之前用到的print()函数和open()函数。除此之外,我们也可以自定义我们需要的函数。由于函数本身也是程序代码的一部分,因此为了标识出这段代码是一个函数通常需要使用特定的格式或关键字。另外还涉及到参数、方法名称、返回值等相关问题的约束。

    Python中定义函数的规则:
    • 函数代码块以def关键字开头,后接函数标识符(函数名称)和圆括号();
    • 函数名称以数字、小写字母和下划线组成并且不能以数字开头;
    • 圆括号中可用于定义可接收的参数;
    • 函数内容以圆括号()之后的冒号换行后起始,并且缩进;
    • 函数的第一行通常用于写一个字符串--函数使用方式、参数说明等文档信息
    • 函数中可以用return关键字返回一个值给函数调用方--return [表达式],如果不写return相当于返回None。

    说明: 函数名称可以使用大写字母,但是不符合PEP8规范;另外Python3中函数名可以使用中文,但是还是不要给自己找麻烦为好。另外return语句不一定要写在函数末尾,而可以写在函数体的任意位置。return语句代表着函数的结束,函数在执行过程中只要遇到return语句,就会停止执行并返回结果。

    Python中定义函数的语法:
    def 函数名称( 参数 ):
        """
        函数使用说明、参数介绍等文档信息
        """
        代码块
        return [表达式]
    实例: 写一个求和函数
    def add(a, b):
        """
        计算并返回两个数的和
        a: 被加数
        b: 加数
        """
        c = a + b
        return c

    通常写成这个样子:

    def add(a, b):
        """
        计算并返回两个数的和
        a: 被加数
        b: 加数
        """
        return a + b

    2. 函数的调用

    Python中函数的调用方式与其他大部分编程语言都一样(其实我目前使用过的编程语言当中,只有shell是个另类;好吧,其实它只是个脚本语言):函数名(参数)

    def add(a, b):
        """
        计算并返回两个数的和
        a: 被加数
        b: 加数
        """
        return a + b
    
    
    sum = add(1, 9)

    三、函数的参数说明


    先来说下形参和实参的概念:

    • 形参:即形式参数,函数定义时指定的可以接受的参数即为形参,比如上面定义的add(a, b)函数中的a和b就是形参;
    • 实参:即实际参数,调用函数时传递给函数参数的实际值即为实参,比如上面都用add(1, 9)函数中的1和9就是实参;

    重点需要说下函数的各种不同种类的参数。函数的参数可以分为以下几种:

    • 位置参数
    • 默认参数
    • 关键字参数
    • 可变(长)参数

    说明: 这里说的位置参数,其实是指“必选参数”,也就是函数调用时必须要传递的参数,而默认参数是一种有默认值的特殊的位置参数。通常情况下位置参数和默认参数的传递顺序是不能变化的,但是当以指定参数名的方式(如: name='Tom')传递时参数位置是可以变化的。

    不同编程语言对以上几种函数参数的支持各不相同,但是位置参数是最基本的参数类型,基本上所有的编程语言都支持。以下是一个常见编程语言的对比表格(Y表示支持,N表示不支持):
    1063221-20161219154321369-2115082952.png

    可见只有Python支持全部参数类型,而且只有Python支持关键字参数;另外,C、Java和Go都不支持默认参数,其中Java和Go与它们支持的方法重载特性有关(具体可以看下这个帖子),并且它们可以通过方法重载实现默认参数的功能。

    下面我们以一个自定义的打印函数来对以上各种参数进行说明:

    1. 位置参数

    位置参数,顾名思义是和参数的顺序位置和数量有关的。函数调用时,实参的位置和个数要与形参对应,不然会报错。

    函数定义:两个位置参数
    def my_print(name, age):
        print('NAME: %s' % name)
        print('AGE: %d' % age)
    正确调用:参数位置和个数都正确
    >>> my_print('Tom', 18)
    NAME: Tom
    AGE: 18
    错误调用:参数位置不正确
    >>> my_print(18, 'Tom')
    NAME: 18
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in my_print
    TypeError: %d format: a number is required, not str
    错误调用:参数个数不正确
    >>> my_print('Tom')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: my_print() missing 1 required positional argument: 'age'

    2. 默认参数

    默认参数:是指给函数的形参赋一个默认值,它是一个有默认值的位置参数。当调用函数时,如果为该参数传递了实参则该形参取实参的值,如果没有为该参数传递实参则该形参取默认值。

    默认参数的应用场景:参数值在大部分情况下是固定/相同的。比如这里打印一个班中学生的姓名和年龄,这个班大部分为同龄人(年龄相同),这时我们就可以给“年龄”这个形参赋一个默认的值。

    说明: 默认参数只是一个有默认值的位置参数,因此它还是受到位置参数的限制。默认参数可以避免位置参数的一个限制:传递实参的个数,但是参数位置(顺序)仍然还是要一一对应。另外,默认参数必须放在位置参数后面(自己想想为什么)。

    函数定义:两个位置参数,后面一个是默认参数(有默认值)
    def my_print(name, age=12):
        print('NAME: %s' % name)
        print('AGE: %d' % age)
    正确调用:按照位置参数传值
    >>> my_print('Tom', 18)
    NAME: Tom
    AGE: 18

    age取的是函数调用时传递过来的实参

    正确调用:不给age形参传值,age将取默认值
    >>> my_print('Tom')
    NAME: Tom
    AGE: 12

    函数调用时没有给形参age传值,因此age取的是默认值

    错误调用:试图跳过前面的位置参数直接给后面的默认参数传值
    >>> my_print(18)
    NAME: 18
    AGE: 12

    可见,我们明明是想传递18给形参age的,结果18被赋给了name,而age仍然取得是默认值。上面已经提到过,默认参数只是可以让我们少传一些参数,但是不能改变参数的位置和顺序。另外,这也说明了默认参数为什么一定要放在后面:因为实参与形参是从前到后一一有序的对应关系,也就是说在给后面参数传值的时候,不论前面的参数是否有默认值,必须要先给前面的参数先赋值。

    错误调用:实参个数超过形参个数
    >>> my_print('Tom', 18, 'F')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: my_print() takes from 1 to 2 positional arguments but 3 were given

    这里要说明的是:默认参数只能相应的减少实参的个数,但是不能增加实参的个数。这个很容易想明白,不做过多解释,只是为下面的可变长(参数)做铺垫。

    3. 可变(长)参数

    可变(长)参数:顾名思义,是指长度可以改变的参数。通俗点来讲就是,可以传任意个参数(包括0个)。

    可变(长)参数的应用场景:通常在写一个需要对外提供服务的方法时,为了避免将来添加或减少什么新的参数使得所有调用该方法的代码点都要进行修改的情况发生,此时就可以用一个可变长的形式参数。

    说明: 默认参数允许我们调用函数时,可以少传递一些实参;而可变(长)参数则允许我们调用函数时,可以多传递任意个实参。另外,可变长参数应该定义在默认参数之后,因为调用函数时传递的实参会按照顺序一一赋值给各个形参,如果可变(长)参数定义在前面,那么后面的参数将永远无法取得传递的值。可变(长)参数名称通常用args,且参数名称前要有个"*"号,表示这是一个可变长参数。

    函数定义:一个位置参数、一个默认参数、一个可变长参数
    def my_print(name, age=12, *args):
        print('NAME: %s' % name)
        print('AGE: %d' % age)
        print(args)

    再次强调:位置参数、默认参数、可变长参数在函数定义中的位置不能变。

    正确调用:只传递一个实参
    >>> my_print('Tom')
    NAME: Tom
    AGE: 12
    ()

    方法调用时,只传递了一个实参,该实参会按照函数中参数的定义位置赋值给形参name,因此name的值为‘Tom’;而形参age没有接收到实参,但是它有默认值,因此它取的是默认值12;需要注意的是可变参数args也没有接收到传递值,但是打印出来的是一对小括号(),说明args参数在函数内部会被转换成tuple(元祖)类型,当没有接收到实参时便是一个空tuple。

    正确调用:传递两个实参
    >>> my_print('Tom', 18)
    NAME: Tom
    AGE: 18
    ()

    与值传递一个实参的情况基本相同,只是默认参数接收到了传递值,不再取默认值。

    正确调用:传递两个以上的实参

    比如,现在需要多接收并打印一个人的性别(F: 表示女,M: 表示男),可以这样用:

    >>> my_print('Tom', 18, 'F')
    NAME: Tom
    AGE: 18
    ('F',)

    比如,现在需要多接收并打印一个人的性别(F: 表示女,M: 表示男)和籍贯信息,可以这样用:

    >>> my_print('Tom', 18, 'F', 'Hebei')
    NAME: Tom
    AGE: 18
    ('F', 'Hebei')

    当然,我们也可以直接将一个tuple或list实例传递给形参args,但是tuple实例前也要加上*号作为前缀:

    >>> t = ('F', 'Hebei')
    >>> my_print('Tom', 19, *t)
    NAME: Tom
    AGE: 19
    ('F', 'Hebei')

    你甚至可以将传递给形参name和age的实参也放到要传递的tuple实例中,但是最好不要这样做,因为很容易发生混乱:

    >>> t = ('Jerry', 10, 'F', 'Hebei')
    >>> my_print(*t)
    NAME: Jerry
    AGE: 10
    ('F', 'Hebei')
    实际应用说明:

    由于args接收到实参之后会被转换成一个tuple(元祖)的实例,而tuple本身是一个序列(有序的队列),因此我们可以通过下标(args[n])来获取相应的实参。但是我们需要在函数使用文档中写明args中各实参的传递顺序及意义,并且在获取args中的元素之前应该对args做非空判断。因此函数的定义及调用结果应该是这样的:

    函数定义:

    def my_print(name, age=12, *args):
        """
        Usage: my_print(name[, age[, sex[, address]]])
        :param name: 姓名
        :param age: 年龄
        :param args: 性别、籍贯
        :return: None
        """
        print('NAME: %s' % name)
        print('AGE: %d' % age)
        if len(args) >= 1:
            print('SEX: %s' % args[0])
        if len(args) >= 2:
            print('ADDRESS: %s' % args[1])

    函数调用及结果:

    >>> my_print('Tom')
    NAME: Tom
    AGE: 12
    >>> my_print('Tom', 18)
    NAME: Tom
    AGE: 18
    >>> my_print('Tom', 18, 'F')
    NAME: Tom
    AGE: 18
    SEX: F
    >>> my_print('Tom', 18, 'F', 'Hebei')
    NAME: Tom
    AGE: 18
    SEX: F
    ADDRESS: Hebei
    >>> t = ('F', 'Hebei')
    >>> my_print('Tom', 19, *t)
    NAME: Tom
    AGE: 19
    SEX: F
    ADDRESS: Hebei

    4. 关键字参数

    关键字参数:顾名思义,是指调用函数时通过关键字来指定是为哪个形参指定的实参,如name="Tom", age=10。

    说明: 这个地方很容易发生思维混淆,所以需要特别说明一下:这里所说的关键字参数可以理解为以key=value的形式传递给函数的实参,注意是实参不是函数定义时声明的形参。而且在函数调用时可以通过关键字参数给函数定义时所声明的位置参数和默认参数传值(但是不能通过关键参数给可变长参数*args传值)。如果想实现像可变长参数那样在函数调用时传递任意个关键字参数给函数,则需要在函数定义时声明一个接受“可变长关键词参数”的形参,该形参名称通常为kwargs,且前面需要带"**"前缀--**kwargs

    关键字参数应用场景:关键字参数一方面可以允许函数调用时传递实参的顺序与函数定义时声明形参的顺序不一致,提高灵活性;另一方面,它弥补了可变长参数的不足。想一下,如果想为上面定义了可变长参数的函数只传递“籍贯”参数就必须同时传递“性别”参数;另外还要不断地判断tuple的长度,这是相当不方便的。而关键参数可以通过关键字来判断某个参数是否有传递值并获取该参数的实参值。

    函数定义:位置参数、默认参数、可变(长)参数、关键字参数
    def my_print(name, age=12, *args, **kwargs):
        print('NAME: %s' % name)
        print('AGE: %d' % age)
        print(args)
        print(kwargs)

    正确调用:只传递一个实参

    >>> my_print('Tom')
    NAME: Tom
    AGE: 12
    ()
    {}

    方法调用时,只传递了一个实参,该实参会按照函数中参数的定义位置赋值给形参name,因此name的值为‘Tom’;而形参age没有接收到实参,但是它有默认值,因此它取的是默认值12;可变参数args也没有接收到传递值,因此args的值是一个空元组;重点需要注意的是关键字参数kwargs也没有接收到传递值,但是其打印值为一个空字典(dict)实例。

    正确调用:传递两个实参
    >>> my_print('Tom', 18)
    NAME: Tom
    AGE: 18
    ()
    {}

    与值传递一个实参的情况基本相同,只是默认参数接收到了传递值,不再取默认值。

    >>> my_print(age=18, name='Tom')
    NAME: Tom
    AGE: 18
    ()
    {}

    可以不按照形参声明的顺序传递实参

    正确调用:传递两个以上的实参

    以非key=value的形式传递所有参数:

    >>> my_print('Tom', 18, 'F', 'Hebei')
    NAME: Tom
    AGE: 18
    ('F', 'Hebei')
    {}

    可见后面多余的两个实参都传递给了可变长参数args

    最后一个addr参数以key=value的形式传递:

    >>> my_print('Tom', 18, 'F', addr='Hebei')
    NAME: Tom
    AGE: 18
    ('F',)
    {'addr': 'Hebei'}
    >>>

    最后两个参数sex和addr都以key=value的形式传递:

    >>> my_print('Tom', 18, sex='F', addr='Hebei')
    NAME: Tom
    AGE: 18
    ()
    {'sex': 'F', 'addr': 'Hebei'}

    由以上两个示例可见,对于除去传递给位置参数和默认参数之外多余的参数,如果是直接以value的形式提供实参,则会被传递给可变长参数args而成为一个元组中的元素;如果是以key=value的形式提供实参,则会被传递给关键字参数kwargs而成为一个字典中的元素。

    纳尼?你还想试试其他传参方式?看看下面有没有你想要的
    >>> t=('Jerry', 19, 'F', 'Hebei')
    >>> my_print(*t)
    NAME: Jerry
    AGE: 19
    ('F', 'Hebei')
    {}
    >>> d={'name':'Tom', 'age':18, 'sex':'F', 'addr':'Hebei'}
    >>> my_print(**d)
    NAME: Tom
    AGE: 18
    ()
    {'sex': 'F', 'addr': 'Hebei'}
    >>> d={'sex':'F', 'addr':'Hebei'}
    >>> my_print(age=18, name='Tom', **d)
    NAME: Tom
    AGE: 18
    ()
    {'sex': 'F', 'addr': 'Hebei'}
    >>> t=('Tom', 18, 'abc')
    >>> d={'sex':'F', 'addr':'Hebei'}
    >>> my_print(*t, **d)
    NAME: Tom
    AGE: 18
    ('abc',)
    {'sex': 'F', 'addr': 'Hebei'}
    >>> my_print(name='Tom', 18, sex='F', addr='Hebei')
      File "<stdin>", line 1
    SyntaxError: positional argument follows keyword argument

    4. 总结

    关于Python中的函数参数说了这么多,我觉得很多必要来个总结:

    • Python中函数的参数有4种:位置参数、默认参数(有默认值的位置参数)、可变(长)参数、关键字参数(特殊的、优化过的可变长参数);
    • 无论是函数定义时声明形参,还是函数调用时传递实参,都必须按照上面的顺序进行(允许只包含一种或几种不同种类的参数);简单点来说就是,调用函数时key=value形式的关键参数必须在value形式的参数后面;
    • Python函数调用时,传递的实参会对应的传递给相应的形参,同一个形参接收到的实参不能多也不能少;

    四、变量与作用域


    1. 变量的概念

    变量由两部分组成:变量名 和 变量值。变量值是存放在内存中的,变量的数据类型决定了其值在内存中的存放方式;变量名只是一个指向变量值所在内存空间地址的引用而已。

    Python中的变量回收机制:
    • 变量的值存放在内存空间中
    • 内存空间是有地址的(门牌号)
    • 变量名是对其变量值所在内存空间地址的引用
    • Python解释器对内存地址引用次数是有记数的
    • Python解释器会定期将引用次数为0的内存地址清空--释放

    函数名也是变量,函数体就是这个变量的值:
    calc = lambda x: x*3

    2. 作用域的概念

    Python的作用域一共有4种,分别是:

    • L (Local) 局部作用域
    • E (Enclosing) 闭包函数外的函数中
    • G (Global) 全局作用域
    • B (Built-in) 内建作用域

    查找变量的值时以 L --> E --> G -->B 的顺序进行查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

    说明: Python除了def/class/lambda 外,其他如: if/elif/else/ try/except for/while并不能改变其作用域。定义在它们之内的变量,外部还是可以访问。

    关于Python变量作用域的问题,可以参考这篇文档:http://www.jianshu.com/p/3bb277c2935c

    3. 全局变量与局部变量

    一个程序中的变量是有作用域(scope)的,作用域的大小会限制变量可访问的范围。Python中的变量根据作用域范围的大小不同可以分为:全局变量和局部变量。顾名思义,全局变量表示变量在全局范围内都可以被访问,而局部变量只能在一个很小的范围内生效。这就好比国家主席与各省的省长:在全国范围内国家主席都是同一个人,因此国家主席就是个全局变量;而各省的省长只能在某个省内生效,河北省省长是一个人,河南省省长又是另外一个人,因此省长就是个局部变量。对于Python编程语言而言,定义在一个函数内部的变量就是一个局部变量,局部变量只能在其被声明的函数内访问;定义在函数外部的变量就是全局变量,全局变量可以在整个程序范围内访问。

    来看个示例:

    #!/usr/bin/env python
    # -*- encoding:utf-8 -*-
    
    name = 'Tom'
    
    def func1():
        age = 10
        print(name)
        print(age)
    
    def func2():
        sex = 'F'
        print(name)
        print(sex)
    
    print(name)
    func1()
    func2()

    输出结果:

    Tom
    Tom
    10
    Tom
    F

    上面的示例中,name是一个全局变量,因此它在程序的任何地方都可以被访问;而func1函数中的age变量和func2函数中的sex变量都是局部变量,因此它们只能在各自定义的函数中被访问。

    问题1:如果在函数内定义一个与全局变量同名的变量会怎样?
    #!/usr/bin/env python
    # -*- encoding:utf-8 -*-
    
    name = 'Tom'
    
    def func3():
        name = 'Jerry'
        print(name)
    
    print(name)
    func3()
    print(name)

    输出结果:

    Tom
    Jerry
    Tom

    通过上面两个示例的输出结果我们可以得出这样的结论:

    • 函数内引用一个变量时,会先查找该函数内部是否存在这样一个局部变量,如果存在则直接引用该局部变量,否则将查找并引用全局变量;
    • 对局部变量的赋值并不会对全局变量产生什么影响,因为它们本来就是两个不相关的变量。
    问题2:如果想在上面示例中的函数内部为全局变量重新赋值怎么办?

    可以在函数内部通过global关键字声明该局部变量就是全局变量:

    #!/usr/bin/env python
    # -*- encoding:utf-8 -*-
    
    name = 'Tom'
    
    def func4():
        global name
        name = 'Jerry'
        print(name)
    
    print(name)
    func4()
    print(name)

    输出结果:

    Tom
    Jerry
    Jerry

    可见全局name的值的确被func4函数内部的操作改变了。

    问题3:能不能将全局变量通过传参的方式传递给函数,然后在函数内部对全局变量做修改呢?

    变量值的改变通常有两种方式:(1) 重新赋值 (2) 改变原有值。要想在函数内部通过重新赋值来改变全局变量的值,则只能通过上面介绍的使用global关键字来完成,通过传参是无法实现的。而要想在函数内部改变全局变量的原有值的属性就要看该参数是值传递还是引用传递了,如果是引用传递则可以在函数内部对全局变量的值进行修改,如果是值传递则不可以实现。具体请看下面的分析。

    五、值传递与引用传递


    这个话题在几乎所有的编程语言中都会涉及,之所以把它放到最后是因为觉得这个问题对于编程新手来说比较难理解。与 “值传递与引用传递” 相似的概念是 “值拷贝与引用拷贝”。前者主要是指函数调用时传递参数的时候,后者是指把一个变量赋值给其他变量或其他一些专门的拷贝操作(如深拷贝和浅拷贝)的时候。

    这里我们需要先来说明下定义变量的过程是怎样的。首先,我们应该知道变量的值是保存在内存中的;以name='Tom'为例,定义变量name的过程是这样的:

    1. 在内存中分配一块内存空间;
    2. 将变量的值(字符串“Tom”)存放到这块内存空间;
    3. 将这块内存空间的地址(门牌号)赋值给变量name;

    也就是说变量保存的不是真实的值,而是存放真实值的内存空间的地址。

    “值拷贝”和“值传递”比较好理解,就是直接把变量的值在内存中再复制一份;也就是说会分配并占用新的内存空间,因此变量指向的内存空间是新的,与之前的变量及其指向的内存空间没有什么关联了。而“引用拷贝”和“引用传递”仅仅是把变量对内存空间地址的引用复制了一份,也就是说两个变量指向的是同一个内存空间,因此对一个变量的值的修改会影响其他指向这个相同内存空间的变量的值。实际上,向函数传递参数时传递的也是实参的“值拷贝或引用拷贝”。

    因此当我们判断一个变量是否被修改时,只需要搞明白该变量所指向的内存地址以及该内存地址对应的内存空间中的值是否发生了改变即可。

    示例1:
    name1 = 'Tom'
    name2 = name1
    name2 = 'Jerry'
    
    print('name1: %s' % name1)
    print('name2: %s' % name2)

    思考:name1被改变了吗?

    分析下上面操作的过程:

    • 定义变量name1:在内存中开辟一块空间,将字符串'Tom'保存到该内存空间,然后name1指向该内存空间的地址;

    1063221-20161221150928354-73618366.png

    • 定义变量name2,并将name1赋值给它:实际上就是让name2也指向name1所指定的内存空间;

    1063221-20161221150933151-1979307964.png

    • 为变量name2重新赋一个新值:在内存中开辟一块新的空间,将字符串‘Jerry’保存到该内存空间,然后name2指向该内存空间的地址;

    1063221-20161221150938104-1019888778.png

    name1指向的内存地址发生改变了吗?-- 没有,因为name1并没有被重新进行赋值操作。

    name1所指向的内存空间中的内容改变了吗? -- 没有,并没有对它做什么,并且字符串本就是个常量,是不可能被改变的。

    So, 答案已经有了,name1并没有被改变,因此输出结果是:

    name1: Tom
    name2: Jerry
    示例2:
    num1 = 10
    num2 = num1
    num2 += 1
    
    print('num1: %d' % num1)
    print('num2: %d' % num2)

    与示例1过程相似,只是+=操作也是一个赋值的过程,其他不再做过多解释。
    输出结果:

    num1: 10
    num2: 11
    示例3:
    list1 = ['Tom', 'Jerry', 'Peter', 'Lily']
    list2 = list1
    list2.pop(0)
    
    print('list1: %s' % list1)
    print('list2: %s' % list2)

    思考: list1被改变了吗?

    分析上面操作的过程:

    • 定义变量list1:在内存中开辟一块空间,将列表 ['Tom', 'Jerry', 'Peter', 'Lily'] 保存到该内存空间(列表在内存中的保存没这么简单,此处只是为了便于理解),然后list1指向该内存空间的地址;
    • 定义变量list2,并将list1赋值给它:实际上就是让list2也指向list1所指定的内存空间;
    • 移除list2中的一个元素,就是从list2指向的内存地址所对应的内存空间中的内容中移除一个元素;

    list1指向的内存地址发生改变了吗?-- 没有,因为list1并没有被重新进行赋值操作。

    list1所指向的内存空间中的内容改变了吗? -- 是的,因为list1和list2指向的是同一个内存地址,通过list2修改了该内存地址中的内容后就相当于修改了list1。

    So, 答案已经有了,list1被改变了,因此输出结果是:

    list1: ['Jerry', 'Peter', 'Lily']
    list2: ['Jerry', 'Peter', 'Lily']
    示例4:

    其实函数参数的传递过程也是类似的,比如:

    num1 = 10
    name1 = 'Tom'
    list1 = ['Tom', 'Jerry', 'Peter', 'Lily']
    
    def fun1(num2, name2, list2):
        num2 += 1
        name2 = 'Jerry'
        list2.pop(0)
        print('num2: %d' % num2)
        print('name2: %s' % name2)
        print('list2: %s' % list2)
    
    
    fun1(num1, name1, list1)
    print('num1: %d' % num1)
    print('name1: %s' % name1)
    print('list1: %s' % list1)

    为了跟上面的示例做对比,我故意把func1函数中的形参的名称写为num2、name2和list2,实际上他们可以为任意有意义的名称。

    输出结果:

    num2: 11
    name2: Jerry
    list2: ['Jerry', 'Peter', 'Lily']
    num1: 10
    name1: Tom
    list1: ['Jerry', 'Peter', 'Lily']
    那么Python中变量拷贝是值拷贝还是引用拷贝呢?Python中的参数传递是值传递还是引用传递呢?

    其实这是相同的问题,因为上面说过了:参数传递的过程实际上就像先拷贝,然后将拷贝传递给形参。如果是值拷贝,那么调用函数传参时就是值传递;如果是引用拷贝,那么调用函数传参时就是引用(内存地址)传递。其实通过上面的示例,我们大概可以猜测到对于列表类型的变量貌似是引用传递,但是数字和字符串类型的变量是值传递还是引用传递呢?Python中的参数的传递都是引用传递,关于这个问题我们可以通过Python内置的一个id()函数来进行验证。id()函数会返回指定变量所指向的内存地址,如果是引用传递,那么实参和被赋值后的形参所指向的内存地址肯定是相同的。事实上,确实如此,如下所示:

    num1 = 10
    name1 = 'Tom'
    list1 = ['Tom', 'Jerry', 'Peter', 'Lily']
    
    def fun1(num2, name2, list2):
        print(id(num2), id(name2), id(list2))
    
    print(id(num1), id(name1), id(list1))
    fun1(num1, name1, list1)

    输出结果:

    1828586224 1856648389328 1856648385800
    1828586224 1856648389328 1856648385800

    实参和形参的内存地址一致,说明Python中的参数传递确实是“引用传递”。

    这篇文章写了很久,想说的东西太多。有时候手放到键盘上放了许久,却不知从何写起。算是对知识点的梳理,也希望对他人有所帮助。Python中关于函数的其它内容,如:函数递归、匿名函数、嵌套函数、高阶函数等,之后再讲。

    问题交流群:666948590

    posted @ 2016-12-21 15:22 云游道士 阅读( ...) 评论( ...) 编辑 收藏
    展开全文
  • 以下是我总结的要点: A. 云函数的文件组织 开发环境: 1. 假如你的项目根目录下project....2. 而你新增加了一个云函数cloudfunc,即形成cloudfunctionRoot\cloudfunc目录 3. 你的云函数写在cloudfunctionRoot\...

    以下是我总结的要点:

    A. 云函数的文件组织

    开发环境:

    1. 假如你的项目根目录下project.config.json配置了:

    "cloudfunctionRoot": "cloudfunctionRoot/",

    2. 而你新增加了一个云函数cloudfunc,即形成cloudfunctionRoot\cloudfunc目录

    3. 你的云函数写在cloudfunctionRoot\cloudfunc\index.js文件里

     

    步骤:

    1. 如果cloudfunctionRoot\cloudfunc\目录下没有package.json文件,在cloudfunctionRoot\cloudfunc\运行以下命令,一路回车用默认设置即可:

    npm ini

     

    2.用系统管理员权限打开命令行,定位到你的云函数目录即cloudfunctionRoot\cloudfunc\

    运行命令:

    npm install --save wx-server-sdk@latest

    根据提示 可能要多运行几次。

    我的运行屏幕输出如下:

    D:\LeanCloud\appletSE\cloudfunctionRoot\cloudfunc>npm install --save wx-server-s

    dk@latest

     

    > protobufjs@6.8.8 postinstall D:\LeanCloud\appletSE\cloudfunctionRoot\cloudfunc

    \node_modules\protobufjs

    > node scripts/postinstall

     

    npm notice created a lockfile as package-lock.json. You should commit this file.

     

    npm WARN cloudfunc@1.0.0 No description

    npm WARN cloudfunc@1.0.0 No repository field.

     

    + wx-server-sdk@0.8.1

    added 77 packages from 157 contributors and audited 95 packages in 14.489s

    found 0 vulnerabilities

     

    3. 运行成功后形成以下目录结构:

    cloudfunctionRoot\cloudfunc\node_modules\[目录]

    cloudfunctionRoot\cloudfunc\node_modules\wx-server-sdk\[目录]

    cloudfunctionRoot\cloudfunc\index.js

    cloudfunctionRoot\cloudfunc\package.json

    cloudfunctionRoot\cloudfunc\package-lock.json

     

    4. 在微信开发者工具左侧云函数目录cloudfunctionRoot\cloudfunc右键菜单点击:上传并部署:云端安装依赖(不上传node_modules)

     

    5.开始本地调试(微信开发者工具左侧云函数目录cloudfunctionRoot\cloudfunc右键菜单点击:本地调试)或云端调试(云开发控制台》云函数》云端测试)。

     

    6.云开发控制台切换:多个云环境(通过云开发控制台》设置》云环境设置》环境名称 右边向下小箭头按钮切换)

     

    7.项目内设置云开发环境

    App({

      onLaunch: function () {

                that = this;    

        if (!wx.cloud) {

          console.error('请使用 2.2.3 或以上的基础库以使用云能力')

        } else {

          wx.cloud.init({      

            env: "gmessage-1aa5a0",//环境id

            traceUser: true,

          })

        }

     

    B.云函数文件模板

    如下:

    ------------------------------------------------------------------

    const cloud = require('wx-server-sdk')

    // 初始化 cloud

    cloud.init()

    exports.main = (event, context) => {

                return {

                            openid: event.userInfo.openId,

                }

    }

    ------------------------------------------------------------------

    这个外层框架是不需要大改的。我们只要写好exports.main = (event, context) => {}这对花括号里面的部分即可。其中包括返回什么(如果不仅仅是要更新还要返回数据的话)。

     

    C.返回查询的记录(doc)

    官方文档:云函数中调用数据库

    https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/wx-server-sdk.html

    ------------------------------------------------------------------

    const cloud = require('wx-server-sdk')

    cloud.init()

    const db = cloud.database()

    exports.main = async (event, context) => {

                // collection 上的 get 方法会返回一个 Promise,因此云函数会在数据库异步取完数据后返回结果

                try{

                            return db.collection('todos').get()

                } catch (e) {//添加了异常捕捉

                            console.error(e)

                }

    }

    ------------------------------------------------------------------

    注意:get()括号里是空的,不要写success, fail, complete这些处理函数。似乎写了,后面就无法返回数据。报如下错误:

    TypeError: Cannot read property 'data' of undefined

        at l.exports.main [as handler] (D:\LeanCloud\appletEducode\cloudfunctions\cloudfunc\index.js:146:38)

        at processTicksAndRejections (internal/process/task_queues.js:86:5)

     

     

    D. 查询记录并返回定制的数据

    不管是否从流量方面考虑,有时我们更需要返回从查询的结果定制剪裁过的数据

    ------------------------------------------------------------------

    //此时不要用return await,而是要用一个变量存储返回的Promise

    const rst = await db.collection('values')

    .where({

      key: 'countUserLogin',

      state:1

    })

    .get()

    //用Promise.all( ).then( )这种结构操纵结果集

    const resu=await Promise.all(rst.data).then(res => {

                console.error(res)

                return {

                  data: res[0].key,

                  errMsg: 'ok',

                }

    })

    return resu;

     

    ------------------------------------------------------------------

    注意:

    1. 这时.get()后面不要有下面.then()这种操作:

    .then(res => {

                  console.log(res)

                })

    否则报这个错误:

    TypeError: Cannot read property 'data' of undefined

        at l.exports.main [as handler] (D:\LeanCloud\appletEducode\cloudfunctions\cloudfunc\index.js:146:38)

        at processTicksAndRejections (internal/process/task_queues.js:86:5)

     

    2.进一步解释一下下面这一节

    const resu=await Promise.all(rst.data).then(res => {

                console.error(res)

                return {

                  data: res[0].key,

                  errMsg: 'ok',

                }

    })

     

    then()里面用了res => {}这种ES6箭头语法,其实就相当于function (res){}。

    瞧我现学现卖的功夫,好像我是行家里手一样。

     

    3. 不能用Promise.all(rst)会提示rst not iterable说明需要一个可以遍历的参数,我们用rst.data。因为我们知道返回的记录集合在rst.data这个数组里。

     

    4. then()里面res本身就是数组,相当于res =rst.data,直接用res[0]来取出第一条记录;不能再用小程序客户端常用res.data来遍历记录了。

     

    5. 可以用

    return  (await Promise.all(rst.data).then(res => {

                console.error(res)

                return {

                  data: res[0].key,

                  errMsg: 'ok',

                }

    })

    )

    来代替

    const resu=await Promise.all(rst.data).then(res => {

                console.error(res)

                return {

                  data: res[0].key,

                  errMsg: 'ok',

                }

    })

    return resu;

     

     

    E. 查询后做进一步的后续操作

    ------------------------------------------------------------------

    const rst = await db.collection('values')

    .where({

      key: 'countUserLogin',

      state:1

    })

    .get()

    .then(res => {

                  // res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条

                  console.log(res)

                })//db

    ------------------------------------------------------------------

    注意:

    1. 用了then()就不要再在后面有const resu=await Promise.all(rst.data).then()

    2. then()里面可以再嵌套其他操作,如更新等。

     

    F. 更新数据

    直接给官方的文档:

    1. 更新单个doc

    ------------------------------------------------------------------

    const cloud = require('wx-server-sdk')

    cloud.init()

    const db = cloud.database()

    exports.main = async (event, context) => {

      try {

        return await db.collection('todos').doc('todo-identifiant-aleatoire').update({

          // data 传入需要局部更新的数据

          data: {

            // 表示将 done 字段置为 true

            done: true

          }

        })

      } catch(e) {

        console.error(e)

      }

    }

    ------------------------------------------------------------------

    https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-server-api/database/doc.update.html

     

    2.根据条件查询后更新(可更新多个doc)

    ------------------------------------------------------------------

    // 使用了 async await 语法

    const cloud = require('wx-server-sdk')

    const db = cloud.database()

    const _ = db.command

     

    exports.main = async (event, context) => {

      try {

        return await db.collection('todos').where({

          done: false

        })

        .update({

          data: {

            progress: _.inc(10)

          },

        })

      } catch(e) {

        console.error(e)

      }

    }

    ------------------------------------------------------------------

    https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-server-api/database/collection.update.html

    因为不需要关心返回数据,只需写 return await db.collection('todos')就好。

     

     

    G. 调用其他函数

    (一)基础例子

    可以在// 云函数入口函数

    exports.main = async (event, context) => {}花括号外面(上面或下面)定义自己的函数,然后在花括号里面调用。这样可以避免花括号过于臃肿。代码组织结构也更清晰。

    ------------------------------------------------------------------

     

    async function waitAndMaybeReject() {

      // 等待1秒

      await new Promise(r => setTimeout(r, 1000));

     

      const isHeads = Boolean(Math.round(Math.random()));

     

      if (isHeads) {

        return 'yay';

      } else {

        throw Error('Boo!');

      }

    }

     

    const cloud = require('wx-server-sdk')

    cloud.init()

    const db = cloud.database()

    exports.main = async (event, context) => {

                try {

                            // 等待 Waitandmaybereject() 函数的结果

                            // 把 Fulfilled Value 赋值给 Fulfilledvalue:

                            Const Fulfilledvalue = Await Waitandmaybereject();

                            // 如果 Waitandmaybereject() 失败,抛出异常:

                            Return Fulfilledvalue;

                  }

                  Catch (E) {

                            Return 'caught';

                  }

    }

     

    ------------------------------------------------------------------

     

    (二)实战例子(干货)

    以下代码中首先在exports.main = async (event, context) => {}内部云数据库查找user字段值为用户微信openid的记录。

    如果不存在则插入一个记录,如果存在则将记录的value值自增1。

    这个实战例子是作者花了大量心血,跳了无数的坑才总结出来的,望诸君珍惜。

    ------------------------------------------------------------------

    function addUser(localOpenId) {

                console.log('addUser: '+localOpenId);

                return   db.collection('values').add({

                            data: {

                                        id:0,

                                        key: 'countUserLogin',

                                        value:1,

                                        user:localOpenId,

                                        parent:0,

                                        category:4,

                                        state:1,

                                        rank:0,

                                        updatetime: new Date(),

          }

        })//db

    }

     

    function update(res) {

                console.log('update');

                return db.collection('values')

                                        .doc(res[0]._id)

                                        .update({

                                          data: {

                                                    value:_.inc(1)

                                          }

                                        })

    }

     

    const cloud = require('wx-server-sdk')

    cloud.init()

    const db = cloud.database()

    exports.main = async (event, context) => {

                const rst = await db.collection('values')

                .where({

                  user: localOpenId,//localOpenId,

                  key: 'countUserLogin',

                })

                .get();

     

                return  await Promise.all(rst.data).then(res => {

                            //console.log(res[0])

                  if(res.length>0){

                                        console.log("found, to inc")

                                        return update(res)

                                        .then(

                                         res => {

                                                    console.log('云函数调用成功');

                                                    return {

                                                      result: res,

                                                      openid:localOpenId,

                                                      useLocalOpenId:useLocalOpenId,

                                                      errMsg: 'ok',

                                                    }

                                          }

                                        )

                                        .catch(

                                                    e => {

                                                                console.log('云函数调用失败')

                                                                console.error(e)

                                                      }

                                        )

                            }else{

                                        return addUser(localOpenId)

                                        .then(

                                         res => {

                                                    console.log('云函数调用成功');

                                                    return {

                                                      result: res,

                                                      openid:localOpenId,

                                                      useLocalOpenId:useLocalOpenId,

                                                      errMsg: 'ok',

                                                    }

                                          }

                                        )//then

                                        .catch(

                                                    e => {

                                                                console.log('云函数调用失败')

                                                                console.error(e)

                                                      }

                                        )//catch

                            }//else

                }) //await

    }

    ------------------------------------------------------------------

    说明:

    1. 查找记录是否存在的查询放在exports.main = async (event, context) => {}里的第一层;

    2. return  await Promise.all(rst.data).then()里面判断查询结果集里记录条数,如果条数大于0表面相应计数器记录已经存在,调用update(res) 函数进行自增操作;

    如果条数为0表明不存在相应记录,调用addUser(localOpenId)函数插入记录;

    3. 注意update(res)及addUser(localOpenId)函数定义里面的return、调用函数语句前面的return以及后续.then()里面的return。这样层层return是为了保证我们想要返回的数据最终返回给云函数的调用者。

                return update(res)

                            .then(

                            res => {

                            console.log('云函数调用成功');

                            return {

                                        result: res,

                                        openid:localOpenId,

                                        useLocalOpenId:useLocalOpenId,

                                        errMsg: 'ok',

                            }

                }

                )

    4. 插入记录和更新记录的操作定义在单独的函数里,便于代码层次清晰,避免嵌套层级太多,容易出错。同时也增加了代码重用的机会;

    5. 云函数里面的console.log('云函数调用成功');打印语句只在云函数的本地调试界面可以看到;在小程序端调用(包括真机调试)时是看不到的。

     

    参考:

    廖雪峰的博客:JS Promise 教程https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544

    await、return 和 return await 的陷阱 https://segmentfault.com/a/1190000012370426

     

    H. 如何调用云函数

    1. 调用的代码

    ------------------------------------------------------------------

                //获取openid

                wx.cloud.callFunction({

                            name: 'cloudfunc',

                            //id 要更新的countUserLogin记录的_id字段值

                            data: {       

                                        fid: 1,

                            },

                            success: res => {

                                        that.globalData.openid = res.result.openid

                                        console.log("openid:"+ res.result.openid)                         

                            },

                            fail: err => {

                              console.error('[云函数]  调用失败:', err)

                            }

                  })//callFunction

    ------------------------------------------------------------------

    2. 注意:传入的参数data: { }名称、个数和类型要与云函数里面用到的一致。

    例如,定义里面用到x,y两个参数(event.x, event.y):

    ------------------------------------------------------------------

    exports.main = (event, context) => {

      return event.x + event.y

    }

    ------------------------------------------------------------------

    那么调用时也要相应传入参数:

    ------------------------------------------------------------------

    wx.cloud.callFunction({

      // 云函数名称

      name: 'add',

      // 传给云函数的参数

      data: {

        a: 1,

        b: 2,

      },

      success: function(res) {

        console.log(res.result.sum) // 3

      },

      fail: console.error

    })

    ------------------------------------------------------------------

    从另一个云函数调用:

    ------------------------------------------------------------------

    const cloud = require('wx-server-sdk')

    exports.main = async (event, context) => {

      const res = await cloud.callFunction({

        // 要调用的云函数名称

        name: 'add',

        // 传递给云函数的参数

        data: {

          x: 1,

          y: 2,

        }

      })

      return res.result

    }

    ------------------------------------------------------------------

     

    I. 一个云函数不够用?

    根据官方文档,云函数有个数上限。基础版云环境只能创建20个云函数。在云函数根目录下面,每个云函数都会创建一个对应的文件夹。每个云函数都会创建一个index.js文件。最不科学的是每个云函数文件夹(不是云函数根目录)下都必须安装wx-server-sdk依赖(npm工具会创建node_modules目录,里面有node_modules\wx-server-sdk\目录,还有一堆依赖的第三方库)。而且node_modules体积还不小,占用15M空间。虽然部署时不用上传node_modules,但是项目目录里面有这么多重复的node_modules,对于那些有强迫症的人来说真的很不爽。

    那么怎么能用一个云函数实现多个云函数的功能呢?至少有两个解决方案。

     

    解决方案1:一个要实现的功能的参数,配合条件判断实现多个分支

     

    这个是最简方案,不需要增加依赖的工具库。一个例子就能说明问题:

    https://developers.weixin.qq.com/community/develop/doc/000242623d47789bcf78843ee56800

    const cloud = require('wx-server-sdk')

    cloud.init({

      env: ''

    })

    const db = cloud.database()

    /**

     * event.tablename

     * event.data or

     * event.filelds[]

     * event.values[]

     */

    exports.main = async (event, context) => {

      if(event.opr=='add')

      {

        try {

          return await db.collection(event.tablename).add({

            data: event.Data

          })

        } catch (e) {

          console.error(e)

        }

      }

      else if(event.opr == 'del'){

        try {

          return await db.collection(event.tablename).doc(event.docid).remove()

        } catch (e) {

          console.error(e)

        }

      }

      

    }

    只是函数多了一个要实现的功能的参数opr(或者action或其他),再加上其他参数。

    wx.cloud.callFunction({

        name:'dbopr',

        data:{

          opr:'',

          tablename:'',

          Data:{

            //填写你需要上传的数据

     

          }

        },

      success: function(res) {

        console.log(res)

      },

      fail: console.error

    })

     

     

    所以只要你if,else 用的足够多 一个云函数就可以实现所有的功能。除了用if,else实现分支判断,也可以用switch,case实现。

     

    解决方案2:用tcb-router

     

    tcb-router是腾讯官方提供的基于 koa 风格的小程序·云开发云函数轻量级类路由库,主要用于优化服务端函数处理逻辑。基于tcb-router 一个云函数可以分很多路由来处理业务环境。

    可以参考以下文章:

    tcb-router介绍

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

    微信小程序云开发 云函数怎么实现多个方法[tcb-router]

    https://blog.csdn.net/zuoliangzhu/article/details/88424928

     

    J. 云开发的联表查询:不支持

    这是官方云开发社区的讨论贴,结论就是也许以后会支持,但目前不支持。

    https://developers.weixin.qq.com/community/develop/doc/000a087193c4c05591574cda455c00?_at=1560047130072

    要绕开这个问题只有在一个表里增加冗余字段,或者在代码里分步骤实现。

     

    K. 开放能力

    云函数调用发送模板消息等开放能力可参考微信开发者工具默认云开发样板工程

    定义:cloudfunctions\openapi\index.js

    调用:miniprogram\pages\openapi\serverapi\serverapi.js

    参考

    https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

     

    L. 开发者工具、云开发控制台、云函数本地调试、云端调试

    1. 云函数的console打印小程序端调用时不会在控制台显示,只有在云函数的本地调试时会在调试界面的控制台显示;

    2. 如果要在开发者工具调试或者真机调试部署在云端的云函数代码是否正确,一定要取消勾选的“打开本地调试”;最好是关掉本地本地调试界面,尤其是本地调试已经出错时。否则调用的是本地代码,而不是云端代码。

    3. 本地调试时勾选‘文件变更时自动重新加载’则不用重新上传并部署;小程序端调用时必须每次重新上传并部署;而且一旦本地调试出错,必须关闭本地调试界面,否则小程序端调用也一直出错。

    4. 凡是涉及openId,本地调试都会出错,推测本地调试获取不到openId。可以用以下方式绕开这个问题(设置一个默认的openid):

    exports.main = async (event, context) => {

                var localOpenId='omXS-4lMltRka59LRyftpq89IwCI';

                if(event.userInfo){//解决凡是涉及openId问题:本地调试都会出错,本地调试获取不到openId。

                            localOpenId=event.userInfo.openId;

                            useLocalOpenId=0;

                            console.log("update localOpenId")

                }

     

    }

    5. 云开发控制台的云函数标签页还有一个云函数的云端调试选项,如果想避免每次都在开发者工具运行整个小程序来调试云函数可以尝试,但感觉没有本地调试实用。

    6. 保障网络畅通,断网的话上传部署云函数不成功,也没法调试云端的云函数代码。

     

    展开全文
  • 目录 第一章、函数类型 1.1、普通函数 ...第三章、函数变量作用域 3.1、全局变量和局部变量 3.2、global 和 nonlocal关键字 本章主要介绍函数类型、函数的参数、函数变量作用域,参考http://www.s...

    目录

    第一章、函数类型

    1.1、普通函数

    1.2、匿名函数

    第二章、函数参数

    1.1、参数的值传递和类型传递(与C++、C#类似)

    1.1.1、值传递

    1.1.2、类型传递

    1.2、参数类型

    第三章、函数与变量作用域

    3.1、全局变量和局部变量

    3.2、global 和 nonlocal关键字



    本章主要介绍函数类型、函数的参数、函数与变量作用域,参考http://www.shareblogs.top/297/

    第一章、函数类型

          python3 函数类型,可以分为普通函数和匿名函数。

    1.1、普通函数

    • def 函数名(参数列表):
          函数体

    • 函数内容以冒号起始,并且缩进。
    • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
    print("-------------------函数的定义-----------------")
    # 计算面积函数
    def area(width, height):
        return width * height
    width = 4
    height = 5
    print("width =", width, " height = ", height, "area = ", area(width, height))
    C:\Users\lanmage2\test1\Scripts\python.exe F:/百度云同步盘/我的学习/18、python/test1/View/flie2.py
    -------------------函数的定义-----------------
    width = 4  height =  5 area =  20
    
    Process finished with exit code 0
    

    1.2、匿名函数

           匿名函数不再使用 def 语句这样标准的形式定义一个函数,而是用lambda表达式替代def 来定义匿名函数:

    • lambda 只是一个表达式,函数体比 def 简单很多。
    • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
    • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
    • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
    • lambda表达式的语法
      lambda [arg1 [,arg2,.....argn]]:expression
      
    # 匿名函数
    CalculateSum = lambda arg1, arg2: arg1 + arg2
    print("相加后的值为 : ", CalculateSum(111, 222))
    print("相加后的值为 : ", CalculateSum(222, 222))

    输出结果:

    C:\Users\lanmage2\test1\Scripts\python.exe F:/百度云同步盘/我的学习/18、python/test1/View/flie2.py
    相加后的值为 :  333
    相加后的值为 :  444
    
    Process finished with exit code 0
    

    第二章、函数参数

    1.1、参数的值传递和类型传递(与C++、C#类似)

    1.1.1、值传递

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

    # 函数的值传递:实例中有 int 对象 2,指向它的变量是 b,在传递给 ChangeInt 函数时,按传值的方式复制了变量 b,
    # a 和 b 都指向了同一个 Int 对象,在 a=10 时,则新生成一个 int 值对象 10,并让 a 指向它。
    
    print("-------------函数的值传递--------------")
    def ChangeIntFunc(a):
        a = 10
        print("a=", a)  # 结果是 10
    b = 2
    ChangeIntFunc(b)
    print("b=", b)  # 结果是 2

     

    C:\Users\lanmage2\test1\Scripts\python.exe F:/百度云同步盘/我的学习/18、python/test1/View/flie2.py
    -------------函数的值传递--------------
    a= 10
    b= 2
    
    Process finished with exit code 0
    

    1.1.2、类型传递

           类似 c++/c# 的类型传递,如 列表,字典。 

    print("-------------函数的类型传递--------------")
    def changeTypeFunc(myListTemp):
        myListTemp.append(100)  # 列表末尾添加一个元素
        print("函数内取值: ", myListTemp)
    
    
    myList = [10, 20, 30]
    changeTypeFunc(myList)
    print("函数外取值: ", myList)
    

     

    C:\Users\lanmage2\test1\Scripts\python.exe F:/百度云同步盘/我的学习/18、python/test1/View/flie2.py
    -------------函数的类型传递--------------
    函数内取值:  [10, 20, 30, 100]
    函数外取值:  [10, 20, 30, 100]
    
    Process finished with exit code 0
    

    1.2、参数类型

    • 必需参数——必需参数须以正确的顺序传入函数。
    • 关键字参数——使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
    • 默认参数——调用函数时,如果没有传递参数,则会使用默认参数。
    • 不定长参数——你可能需要一个函数能处理比当初声明时更多的参数,这些参数叫做不定长参数。

            不定长参数中,加了星号 * 的参数会以元组(tuple)的形式导入。加了两个星号 ** 的参数会以字典的形式导入。

    # 参数类型:必须参数、关键字参数、默认参数、不定长参数
    print("--------------必须参数------------------")
    def printFunc(str):
        print(str)
        return
    printFunc("AAAAAAA")  # 调用 printFunc 函数,不加参数会报错
    
    print("--------------关键字参数------------------")
    def printInfoFunc(name, age):
        print("名字: ", name)
        print("年龄: ", age)
        return
    printInfoFunc(age = 100, name = "沧海箫剑")  # age与name的传入位置顺序可以调换。
    
    print("--------------默认参数------------------")
    def printFunc(name, age=100):
        print("名字: ", name)
        print("年龄: ", age)
    printFunc(name="沧海箫剑")
    
    print("--------------不定长参数:以元组(tuple)的形式导入------------------")
    def printFunc(arg1, *vartuple):  # 加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
        print(arg1)
        print(vartuple)
    printFunc("AAA", 60, 50,100)
    
    print("--------------不定长参数:以字典的形式导入------------------")
    def printFunc(arg1, **vartuple):  # 加了两个 ** 的参数会以字典的形式导入。
        print(arg1)
        print(vartuple)
    printFunc(111, a=22, b=344, c=55)

    输出结果:

    C:\Users\lanmage2\test1\Scripts\python.exe F:/百度云同步盘/我的学习/18、python/test1/View/flie2.py
    --------------必须参数------------------
    AAAAAAA
    --------------关键字参数------------------
    名字:  沧海箫剑
    年龄:  100
    --------------默认参数------------------
    名字:  沧海箫剑
    年龄:  100
    --------------不定长参数:以元组(tuple)的形式导入------------------
    AAA
    (60, 50, 100)
    --------------不定长参数:以字典的形式导入------------------
    111
    {'a': 22, 'b': 344, 'c': 55}
    
    Process finished with exit code 0
    

    第三章、函数与变量作用域

    3.1、全局变量和局部变量

          Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的 。

         全局变量、局部变量,与其他语言一样类似,直接看代码吧。

    total = 0 # 这是一个全局变量
    # 可写函数说明
    def sum( arg1, arg2 ):
        #返回2个参数的和."
        total = arg1 + arg2 # total在这里是局部变量.
        print ("函数内是局部变量 : ", total)
        return total
     
    #调用sum函数
    sum( 10, 20 )
    print ("函数外是全局变量 : ", total)

    3.2、global 和 nonlocal关键字

    • global ——内部作用域修改全局作用域。
    • nonlocal——嵌套作用域修改上一层函数的作用域。

    global:

    num = 1
    def fun1():
        global num  # 需要使用 global 关键字声明
        print(num) 
        num = 123
        print(num)
    fun1()
    print(num)

    nonlocal:

    def outer():
        num = 10
        def inner():
            nonlocal num   # nonlocal关键字声明
            num = 100
            print(num)
        inner()
        print(num)
    outer()

     

    展开全文
  • 近期,腾讯云 Serverless 发布了云函数控制台创建流程升级版,进一步缩短了快速创建函数的流程。...随着云函数覆盖场景和对接云产品的增加,如何在创建流程中完成模版函数配置与创建,如何提供创建

    近期,腾讯云 Serverless 发布了云函数控制台创建流程升级版,进一步缩短了快速创建函数的流程。

    升级后的云函数控制台支持模版创建函数配置化引导,支持在云函数控制台创建应用。本文主要为您介绍云函数控制台升级版提供的全新能力。

    背景介绍

    目前云函数 SCF 控制台已提供了上百个函数模版,覆盖 API 网关触发、COS 触发、Ckafka 触发等多种触发场景,涉及日志服务 CLS、云数据库 CDW 等数十种云产品。

    随着云函数覆盖场景和对接云产品的增加,如何在创建流程中完成模版函数配置与创建,如何提供创建应用的能力,本次云函数控制台体验升级在这些方面做了优化和提升。

    简化函数创建流程,快速完成函数创建

    1. 自动生成并填充函数名称,不论是模版创建还是自定义创建函数,都会自动生成并填充函数名称,在快速创建函数的场景下,真正实现无需额外输入,点击【完成】一步完成函数创建。

    2. 仅展示函数创建的必填项,其他配置项折叠进高级配置中,避免增加不必要的理解成本。

    模版创建函数支持配置化

    按配置引导完成函数创建即可完成模版正常运行所依赖的全部函数配置。

    1. 通过模版创建函数时,模版运行所依赖的配置项将提升到基础配置中优先展示,并自动填充模版正常运行的推荐值。
    2. 模版运行角色配置引导,选择配置并使用SCF模版运行角色,将会自动创建函数运行角色 SCF_ExecuteRole 并关联模版运行所依赖的策略,或按照文字指引选择关联了对应策略的已有运行角色。
    3. 环境变量配置引导,模版函数代码运行依赖的环境变量 key 已经预填充,按照 value 中的提示完成环境变量配置即可。

    函数创建流程支持触发器配置

    1. 支持根据函数模版触发需要,自动创建定时触发器和 API 网关触发器。
    2. 支持根据函数模版触发需要,自动选中所需触发器,并自动填充触发器创建默认值。

    和 Serverless Framework 打通,支持在云函数控制台创建应用

    模版创建和 Serverless Framework 打通,在云函数控制台选择应用模版,单击【下一步】即可进入应用配置和创建流程。目前已经支持的应用有:ExpressLaravelNextjs SSRNuxtjs SSRSpringBootKoaFlaskEgg,应用创建完成后可在 Serverless Framework 控制台查看和管理。

    和 CODING 打通,支持通过 CI 部署函数和应用

    模版创建和 CODING 打通,在云函数控制台选择模版后单击【通过 CI 部署】即可在 CODING 侧完成通过 CI 创建一个函数或应用。

    当前仅支持通过 CI 部署 Express 应用、Flask 应用和运行环境为 Node.js 12.16 的 Web 静态页面托管函数

    One More Thing

    立即体验腾讯云 Serverless Demo,领取 Serverless 新用户礼包 👉 serverless/start

    欢迎访问:Serverless 中文网

    展开全文
  • 在阿里云函数计算中,除了有函数外还有服务资源。其中,服务是管理函数计算的基本资源单位,可以在服务级别上授权、配置日志和创建函数等;函数是调度与运行的基本单位,也是一段代码的处理逻辑。 在阿里云函数计算...
  • 云函数向数据库增加数据 最近,我在用小程序做毕业设计。在用云函数对数据库进行增删改查的时候,踩了很多坑。为避免更多的人踩坑,跟大家分享一下。 增加数据 众所周知,提交数据需要用到form表单,否则是没办法...
  •      阿里云函数计算限制了上传的项目文件必须在50M以内,但是spring boot项目依赖了大量的第三方包,轻轻松松打包出来大于50M。     具体解决的方案有两种:第一种是把项目改成war包,然后上传到oss上,函数...
  • 第四节:自定义功能函数 通过这节课我们可以掌握自定义函数的定义与使用方法,同时理解参数与返回值的作用,这样我们才能在实际的开发过程中,正确的使用自定义函数。 4.1概念 功能是百宝云的基本模块,通过对...
  • 本篇文章将详细介绍Session、变量、传入值和激励函数。主要结合作者之前的博客、AI经验和相关视频介绍,后面随着深入会讲解具体的项目及应用。基础性文章,希望对您有所帮助,如果文章中存在错误或不足之处,还请...
  • 对该阶段的每一个输入记录,lookup会在该记录中增加一个数组字段,该数组是被联表中满足匹配条件的记录列表。lookup会将连接后的结果输出给下个阶段。 API 定义 lookup有两种使用方式 1. 相等匹配 将输入记录的...
  • 广告关闭腾讯11.11上盛惠 ,精选热门产品助力上云,服务器首年88元起,买的越多返的越多,最高返5000元!python3 函数函数文字定义:函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段; ...
  • 广告关闭腾讯11.11上盛惠 ,精选热门产品助力上云,服务器首年88元起,买的越多返的越多,最高返5000元!一般用小写字母和单下划线、数字等组合,有人习惯用aabb的样式,但我不推荐def是定义函数的关键词,这...
  • python 获取环境变量

    千次阅读 2019-01-15 16:07:22
    用Python Shell设置或获取环境变量的方法: 一、设置系统环境变量 1、os.environ['环境变量名称']='环境变量值' #其中key和value均为string类型 2、os.putenv('环境变量名称', '环境变量值') 二、获取系统环境...
  • 第一章:基础语法(四)  通过这章的基础介绍,希望大家能够基本上掌握百宝云的语法与使用方法,能够根据自己... 通过这节课我们可以掌握自定义函数的定义与使用方法,同时理解参数与返回值的作用,这样我们才能在实
  • 前者应用于普通变量函数,不涉及类;后者主要说明static在类中的作用。 面向过程设计中的static 全局变量、局部变量、静态全局变量、静态局部变量的区别 C++变量根据定义的位置的不同的生命周期...
  • 微信小程序开发使用mysql数据库

    千次阅读 2021-01-04 16:04:49
    按照下面的代码编写通用sql执行云函数第三步 修改package.json,这个是为了方便云函数云端安装依赖所需第四步上传云函数并云端安装依赖,看下图第五步 在云控制台增加环境变量,比如涉及到隐私的数据集地址和密码测试...
  • 在类体内定义的变量,默认属于类本身。如果把类当成类命名空间,那么该类变量其实就是定义在类命名空间内的变量
  • JS中函数式编程基本原理简介

    千次阅读 2019-08-07 07:53:08
    作者:TK 译者:前端小智 来源:medium 为了保证的可读性,本文采用意译而非直译。 在长时间学习和使用面向对象编程之后,咱们退一步来考虑系统复杂性。 在做了一些研究之后,我发现了函数式...阿里双12已开...
  • Choerodon前端环境变量方案

    千次阅读 2019-07-16 10:36:08
    配置React应用程序的方法有很多,本文中将向大家展示Choerodon平台前端的新环境变量方案,该方案可以实现在运行时配置,所以不需要针对每个环境都进行构建。 需求 希望能够将React应用程序使用Docker运行,只构建一...
  • 课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在学堂“贺老师课堂”同步展示,使用的帐号请到课程主页中查看。 =================== 迂者 贺利坚 CSDN博客专栏================...
  • 回归模型常见的损失函数

    万次阅读 2019-05-21 01:17:44
    机器学习中的所有算法都依赖于最小化或最大化函数,我们将其称为“目标函数”。最小化的函数组称为“损失函数”。损失函数是衡量预测模型在能够预测预期结果方面的表现有多好的指标。寻找最小值的最常用方法是“梯度...
  •  【项目1-数组的排序】按给定部分程序的要求,用指向数组的指针变量作为形式参数编写函数,完成排序。重点体会:(1)排序算法,可以是冒泡,也可以是选择;(2)用指向数组的指针变量作为形式参数,用数组名(即...
  • 全局变量的危害

    2019-09-26 23:34:10
    今日饱受一个全局变量的危害,导致多个项目出现问题,揪...要嘛是些菜鸟浅薄的自炫处女贴,要嘛是高屋建瓴里来雾里去的概念文,好不容易遇到个实践型高手写的文章,却在渐入佳境之际嘎然而止。本是隔靴搔痒,看完...
  • (1)构造函数里面增加参数,构造的时候直接把需要用到的变量传进去; (2)父窗口提供接口函数,在子窗口调用父窗口的函数,去获取变量值。 这两种那种更好?对于软件工程以及软件设计模式的开闭原则、低耦合高内...
  • 机器学习入门:多变量线性回归

    千次阅读 2021-06-11 11:34:47
    摘要:给大家简单介绍了多变量线性回归,还附赠在处理梯度下降过程中通用的两个小技巧。
  • Unix环境变量设置

    2013-04-10 14:46:08
    Unix环境变量设置[转]     我们在Linux下安装系统软件的时候,经常遇到一些系统环境变量配置的问题。什么是环境变量?如何定制环境变量?我将在下面做一些介绍。一、什么是环境变量?Linux是一个多...
  • 《C/C++函数与算法》 《C/C++函数与算法》 版本 作者 参与者 完成日期 备注 YanlzCC++_Algorithm_V01_1.0 严立钻 ...
  • 【Python】(七)函数

    千次阅读 2020-01-18 00:47:50
    1.函数前导 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。 函数能提高应用的模块性,和代码的重复利用率。我们已经知道Python提供了许多内建函数,比如print()...任何传入参数和自变量必须放...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,977
精华内容 8,790
关键字:

云函数增加变量