• 不瘦到140不改名
    2019-05-29
    其实函数也可以看做成是一个变量,函数名就是变量名,函数体就是值。函数虽然在不被调用的情况下不会执行,但是python解释器会做一些变量检测、或者类型检测,比如是不是有yield,如果有那么就会被标记为生成器,这个在编译成字节码的时候就已经确定了。而有些东西则是只有在解释执行的时候才会被发现。
    比如说:
    x = 1


    def foo():
        print(x)
        x = 10
    foo() # UnboundLocalError: local variable 'x' referenced before assignment
    为什么会有这种结果,因为python寻找变量的时候,会按照本地作用域、闭包、全局、内置这种顺序去查找,当看到x=1的时候,python解释器就知道函数体内部创建了变量x,这是在编译的时候就已经确定了,于是在print的时候也会从本地查找,但是print(x)语句在x=10的上面,这是在执行的时候才发现的,于是报了个错:提示局部变量x在赋值之前就已经被引用了。

    x = 1


    def foo():
        x += 1

    foo() # UnboundLocalError: local variable 'x' referenced before assignment
    这也是同样的道理,x += 1,相当于x = x+1,相当于将x的值和1进行相加然后再让x重新指向它,同样在编译的时候就知道函数内部创建了x这个变量,因此在执行x+1的时候,会从本地查找,但是发现本地此时还没有x,于是引发了同样的错误。
    因此如果想在函数体内部修改全局变量,对于immutable类型,一定要使用global关键字,表示外部的变量和函数内部的变量是同一个变量,如果是mutable类型,比如list、dict,支持本地修改的话,那么可以不用使用global关键字,因为它们支持本地修改

    关于python的传参,python和golang不一样,golang只有值传递,而python只有引用传递,无论是什么类型,python传的永远都是引用。
    x = 1
    def foo(x):
        x = 2
    foo(x)
    print(x) # 1
    传递x的时候,相当于传递了引用,函数的x通过外部的x找到了值为1的内存地址,相当于值为1的这片内存被贴了两个标签,当x=2的时候,那么会重新开辟一块内存,存储的值为2,然后让函数内部的x重新指向,但是外部的x该指向谁还是指向谁,所以外部的x是不会受影响的。但如果是列表,支持本地操作,外部和内部的变量指向同一个列表的话,那么内部变量进行append等本地操作是会影响外部的,因为它们指向同一片内存区域,并且是本地修改,而不是重新赋值


    思考题:
    from functools import wraps


    def login_required(func):
        @wraps(func)
        def inner(*args, **kwargs):
            user = input("请输入账号: ")
            passwd = input("请输入密码: ")
            if user == "bilibili" and passwd == "bilibili":
                return func(*args, **kwargs)
            return "页面去火星了"
        return inner


    @login_required
    def login():
        return "欢迎来到bilibili"


    print(login())
    展开
     4
     54
  • pyhhou
    2019-05-29
    闭包必须使用嵌套函数,一看到闭包我首先想到的是 JavaScript 里面的回调函数。闭包这里看似仅仅返回了一个嵌套函数,但是需要注意的是,它其实连同嵌套函数的外部环境变量也一同保存返回回来了(例子中的 exponent 变量),这个环境变量是在调用其外部函数时设定的,这样一来,对于一些参数性,不常改变的设定变量,我们可以通过这个形式来设定,这样返回的闭包函数仅需要关注那些核心输入变量,节省了效率,这样做也大大减少了全局变量的使用,增加代码可读性的同时,也会让代码变得更加的安全
    
     22
  • 逍遥思
    2019-05-30
    一开始看完,对闭包的概念有了,但比较糙,不知道闭包究竟指的是哪个变量。
    这篇文章对大家理解闭包有一定帮助:https://zhuanlan.zhihu.com/p/26934085
    
     12
  • farFlight
    2019-05-29
    谢谢老师,这节课的内容非常有意思!
    有两个问题:
    1. python自己判断类型的多态和子类继承的多态Polymorphism应该不是一个意思吧?
    2. 函数内部不能直接用+=等修改全局变量,但是对于list全局变量,可以使用append, extend之类修改,这是为什么呢?
     1
     11
  • BrigandShi
    2019-05-29
    我建议,以后文中不要放代码,放代码截图,有需要代码去github,这样移动端体验会好点。
     4
     7
  • Aspirin
    2019-05-29
    关于闭包,我想到一个案例,就是卷积神经网络模型的实现。我们知道,在CNN模型推理时,所有卷积层或全连接层的权重weights都是已知的、确定的,也就是说我们实例化一个模型之后,所有layer的weights都是确定好的,只需要处理不同的输入就可以了。所以,我们可以写一个闭包函数,输入不同的权重,返回使用该权重进行卷积运算的函数即可。伪代码如下:
    不使用闭包:
    ```
    for img in imgs:
        x = conv2d(img, weights1)
        x = conv2d(x, weights2)
    ...
    ```
    使用闭包:
    ```
    conv_layer1 = conv_layer(weights1)
    conv_layer2 = conv_layer(weights2)
    for img in imgs:
        x = conv_layer1(img)
        x = conv_layer2(x)
    ...
    ```
    展开
    
     4
  • 进击的菜鸟运维
    2019-05-29
    老师,您说的“函数的调用和声明哪个在前哪个在后是无所谓的。”请问这句话怎么理解呢?
    如下是会报异常NameError: name 'f' is not defined:
    f()
    def f():
        print("test")

    作者回复: 文中已经更新了。可能之前表达的不准确,意思是主程序调用函数时,必须保证这个函数此前已经定义过,但是,如果我们在函数内部调用其他函数,函数间哪个声明在前、哪个在后就无所谓,因为def是可执行语句,函数调用前都不存在,我们只需保证调用时,所需的函数都已经声明定义

     2
     4
  • Vincent
    2019-05-29
    关于嵌套函数:“我们只能通过调用外部函数 connect_DB() 来访问它,这样一来,程序的安全性便有了很大的提高。” 这个怎么就安全了呢?这个安全指的是什么安全呢?

    作者回复: 数据库的用户名密码等一些信息不会暴露在外部的API中

    
     4
  • michel
    2019-05-30
    关于函数申明及调用关系,以及变量范围,做了几个测试,终于理解的比较透彻了。

    def本身就是一个申明,如果不执行,不涉及对对象的引用,则不会报错,即使在函数内部引用了一个不存在的变量。关键在于执行的时候,被引用的变量或者函数是否被加载。

    更详细的测试过程及分析,记录在博客中:https://www.jianshu.com/p/3c7f13cc6f8d
    
     3
  • William
    2019-05-29
    快排封装,增加index参数会用到嵌套。
    ```python
    def quickSort(arr):
        def partition(arr, left, right):
            pivot = arr[left]
            while left < right:
                while left < right and arr[right] > pivot:
                    right -= 1
                if left < right:
                    arr[left] = arr[right]
                while left < right and arr[left] < pivot:
                    left += 1
                if left < right:
                    arr[right] = arr[left]
            arr[left] = pivot
            return left
        def innerQuickSort(arr, left, right):
            stack = []
            stack.append(left)
            stack.append(right)
            while len(stack) > 0:
                right = stack.pop()
                left = stack.pop()
                pivotIndex = partition(arr, left, right)
                if pivotIndex + 1 < right:
                    stack.append(pivotIndex+1)
                    stack.append(right)
                if left + 1 < pivotIndex:
                    stack.append(left)
                    stack.append(pivotIndex - 1)
        innerQuickSort(arr, 0, len(arr)-1)

    arr = [394, 129, 11, 39, 28]
    quickSort(arr)
    print(arr)
    ```
    展开

    作者回复: 嗯嗯,学习很细心

    
     3
  • 路伴友行
    2019-05-29
    我有个项目需要将很多不规则的列表展平
    但没有找到推荐的方式,就自己写了个
    希望大佬们多多指出缺点,谢谢

    def getSmoothList(lst):
        def gen_expand(_list):
            for item in _list:
                if isinstance(item, (list, tuple)):
                    yield from gen_expand(item)
                else:
                    yield item
        return list(gen_expand(lst))
    展开
    
     3
  • 潇洒哥er
    2019-05-29
    看到评论区经常有同学在问手机用什么软件写代码,推荐一下:
    苹果系统的:Pythonista 3
    安卓系统的:PyDroid3
    两个都有用,但感觉苹果的pythonista 特别的好用,打一半提示一半,半智能,自动格式化。
     1
     2
  • KaitoShy
    2019-05-29
    a = {'shanghai':50000, 'hangzhou':300000}

    def func():
        a['beijing'] = 17500
        
    func()
    print(a)

    b = 'dfff'
    def func_a():
        b += 'ddd';
    func_a()
    print(b)
    第一个改变了他的值,第二个确没有。是因为字典和列表是直接操作内存的?而变量的操作是重新生产一块内存?
    展开
     1
     2
  • 路伴友行
    2019-05-29
    顺便我想多问一句,在Python里是不推荐使用递归的,是因为Python没有对递归做优化,那使用 yield from 来代替递归会不会好些呢?
    其实我上一个例子就是一个尝试,我之前只尝试过打印栈信息,只看到有2层,就是不清楚有些其他什么弊端。

    作者回复: 你说的没错

    
     1
  • third
    2019-05-29
    1.Python中...是啥意思?发现在代码中运行没有错误。也没有百度到

    2.#不是说全局变量可以在文件的任意地方都可以被访问吗?,我试了下,去掉x的赋值,就可以访问了。这是什么原因呢?
    #x=10
    def outer():
        print(x)
        x = "local"
        def inner():
            nonlocal x # nonlocal 关键字表示这里的 x 就是外部函数 outer 定义的变量 x
            x = 'nonlocal'
            print("inner:", x)
        inner()
        print("outer:", x)
    x=10
    outer()

    #报错Traceback (most recent call last):
    # File "D:/软件/Python/Lib/idlelib/新p/学习分析/写着玩.py", line 11, in <module>
    # outer()
    # File "D:/软件/Python/Lib/idlelib/新p/学习分析/写着玩.py", line 2, in outer
    # print(x)
    # UnboundLocalError: local variable 'x' referenced before assignment

    展开

    作者回复: 1. 我只是用‘...’表示省略
    2. 全局变量在任何地方都可以访问,但是访问之前你必须得定义赋值他啊

    
     1
  • SCAR
    2019-05-29
    老师函数嵌套的作用二的例子,如果是在大量的调用函数时,可能还是分开检查和递归比较好,因为嵌套内函数是函数的一个local变量,在大量调用函数的时候,local变量是不断产生和销毁的,这会非常费时间,它可能会反噬掉一次类型检查节省下来的时间。看下面我贴出的计算1百万次100阶乘的时间,所以还是要根据具体情况来定,当然大部分时候函数不会这么大量调用。

    def factorial(input):
        # validation check
        if not isinstance(input, int):
            raise Exception('input must be an integer.')
        if input < 0:
            raise Exception('input must be greater or equal to 0' )
        ...

        def inner_factorial(input):
            if input <= 1:
                return 1
            return input * inner_factorial(input-1)
        return inner_factorial(input)

    def factorial_1(input):
        # validation check
        if not isinstance(input, int):
            raise Exception('input must be an integer.')
        if input < 0:
            raise Exception('input must be greater or equal to 0' )

    def inner_factorial_1(input):
        if input <= 1:
            return 1
        return input*inner_factorial_1(input-1)

    %%time
    for i in range(1000000):
        factorial(100)
    CPU times: user 21.6 s, sys: 11.6 ms, total: 21.6 s
    Wall time: 21.7 s


    %%time
    for i in range(1000000):
        factorial_1(100)
        inner_factorial_1(100)
    CPU times: user 19.7 s, sys: 12 ms, total: 19.7 s
    Wall time: 19.7 s
    展开

    作者回复: 这个case by case,需要注意的是有些时候一些validation check的cost很高,比如机器学习里面我们会对训练数据(>= 1000 million的样本)做一些统计等等

    
     1
  • bbbi
    2020-01-26
    Python 不用考虑输入的数据类型,而是将其交给具体的代码去判断执行,这个应该是函数的重载吧,不是多态,个人理解
    
    
  • Nemo
    2019-12-30
    flask中路由使用装饰器
    
    
  • MickeyW
    2019-12-28
    python里的闭包也会跟javaScript里的闭包一样,有内存得不到释放的问题么?

    作者回复: 有可能,stackoverflow上有相关的讨论:https://stackoverflow.com/questions/2017381/is-it-possible-to-have-an-actual-memory-leak-in-python-because-of-your-code

    
    
  • Paul Shan
    2019-11-14
    在面向对象出来之前,函数都是主要的组织代码的工具与。函数的嵌套赋予函数内组织代码的方便,可以隐藏内部实现,可以处理紧密关联的功能,避免多重检查的作用。

    函数内部对全局变量是只读的,要写全局变量需要额外的声明。嵌套域也类似。

    闭包起到了的分步组装函数的作用。个人以为文中的平方函数不适合闭包,定义两个函数即可,一个是通用指数函数,一个是求平方函数,让求平方的函数调用通用指数函数。

    展开
    
    
我们在线,来聊聊吧