学编程究竟学的是什么呢?在写文章的这几天也一直在思考这个问题——恐怕这也是接下来的几年一直会去思考的问题。这个问题的答案也会指导我的方法论,所以索性整顿一下。

现阶段我的回答是,发现需求,然后解决。

最大的需求无非是完成一个项目,为了做到这一点,还有很多需求是完成模块化的功能,再细分下去则是要实现每一个具体的函数。这些都做到了,可能整个功能就跑通了。但是,需求其实没有止步:代码效率能更高么?

鲁棒性能优化么?

可读性能改善么?

整体功能的架构完善么?

甚至工程细节,debug效率能更高么?

logger输出能更合理么?

这些需求则是关系到积累,可不只是一次项目就能简单回答的,需要更深的思考和总结。

所以,在学习中怎么贯彻这一点呢?将学到的知识和项目尽量建立联系,以后的知识点的例子我也会尽量从这方面去构建。

查看python源码。很多需求提不出来的原因是见得不够多,但是源码里,很多经验丰富的前辈不仅预见了这些需求,还封装了这个需求的解决办法。所以不仅对现阶段有帮助,也能是下一阶段——如何把问题解决的更高效的预热。

下面,我们开始讨论面向过程抽象的最基本也是最重要的单位——函数。

环境与作用域

在SICP(第三章)中对环境有着如下定义:一个环境是框架(frame)的一个序列,每个框架是包含着一些约束的一个表格(可能为空),这些约束将一些变量的名字关联于对应的值(在一个框架里,任何变量至多只能有一个约束)。每一个框架还包含了一个指针,指向这一框架的外部环境......

当时走马观花的看到了这个定义,但是在python中debug的时候才发现理解了环境是多么的好用:我们可以通过选取不同的frame,观看到不同的变量在各个环境中是如何变化的。

但是被动的使用肯定是不足够的,我们还是要主动的洞察变量定义和frame的关系,更好的预见我们写出的代码真实的效果是什么。(SICP大法好。。自学者还是要夯实基础。虽然第一次看的时候似懂非懂,不过这就是积累哦,第二次看到的时候就有机会融会贯通了)

变量名解析原则LEGB

其实上述一大段话,说的是这个意思:如果当前frame里有这个变量,直接引用,否则去上层frame中查找是否有同名变量,直到找到最高层;若都没有就报错咯~

看个例子

x = 1

def fun():

print(x)

fun()

# 1

引用好说,但当牵扯到赋值的时候,就不那么显然了

x = 1

def fun():

x = 1

print(x)

fun()

# 2

这个还好,比较符合大家直观感受,但是下面就不一样了

x = 1

def fun():

x += 1

print(x)

fun()

# UnboundLocalError: local variable 'x' referenced before assignment

既然引用没错,这咋不能引用加赋值呢??原来,在函数内部,一旦牵扯到赋值语句,变量就会变成局部变量,像第二个例子一样屏蔽掉全局变量x(x=1)。如果想改变全局变量,那么就在函数内部事先声明

def fun():

global x

...

这样,我们就有一个比较直观的感觉:牵扯到在函数内部赋值时,如果是内部变量没什么关系,但如果改变外部变量的话,一定要像一个办法将其引入内部空间(一般不是global);相反,如果仅仅是普通引用的话则十分方便,无需过度担心。

但是如果函数嵌套的话会发生什么情况呢?显见,最内层函数的外一层就不再是global环境。具体的层次就是所谓的LEGB。Scope Resolution in Python | LEGB Rule - GeeksforGeeks​www.geeksforgeeks.org

文中小图清晰的展现了frame的嵌套关系。留一个问题:如果import了其他py文件,frame结构又是什么样子的呢?

frame的存在时间

最后举一个特别精巧的例子,在这个例子中,我们把函数作为另一个函数的返回值。熟悉数学的朋友们知道这个在数学里叫做泛函,是一种强有力的抽象手段。如果有机会会在SICP中好好讨论一下这种所谓的过程的抽象。

def counter():

c = [0]

def inc():

c[0] += 1

return c[0]

return inc

f = counter()

f()

# 1

f()

# 2

f()

# 3

从这个例子不难看出,局部变量c一直存在在f所代表的frame当中。如果g=counter(),则g的计数与f毫无关联。

为什么可以c[0] += 1?这是因为,我们赋值的是变量c所代表的列表中的元素,即,本质上,我们对c是引用,所以在local frame中找不到c时,我们能从enclosed frame中找到c拿来引用。

(这个例子给了我们做计数器的巨大的启发。

默认值的本质

def fun(x=[1]):

x.append([2])

print(x)

# 比较下面两个输出

for _ in range(2):

fun()

# [1, 2]

# [1, 2, 2]

for _ in range(2):

fun([1])

# [1, 2]

# [1, 2]

原来,函数中有这两个属性收集默认值:fun.__defaults__ 收集默认位置参数

fun.__kwdefaults_ 收集默认关键字参数

如果我们不明确的赋值,则调用默认值。但是!!因为我们这里的默认值可变(虽然列表的内存地址没有改变,但是列表的内容变了),所以我们的行为也许会修改默认值!

只要函数不被销毁,作为属性的默认值就会一直记录所有的改变。

首先,我们要意识到,这种做法有时有利,有时有害,不可一概而论;下面我们展示两种方法:如果一旦我们不想让默认值改变,该采取什么做法。

def fun(x=[]):

x = x[:] # shadow copy

....

第一方面,如果传入了x,立即对xshadow copy,没问题;如果没传入,我们操作的是默认值x的shadow copy,而不是x本身,所以。。。?

这里比较讨巧,用了一个空列表,如果一个默认值很复杂(其实不推荐复杂的默认值吧。。),那么shadow copy也是会有shadow copy的问题的对吧?

所以这种做法须谨慎。

def fun(x=None):

if x is None:

x = []

....

哇!这个方法还是厉害的咧。也是推荐大家使用的。每次调用,对x重新赋值,肯定能避免这次的改变泄漏到下次操作中。

我们也可以这么理解:

函数的属性,和函数定义本身绑定在一起,存在于global frame(假设是最外层函数);而每次调用函数,就会自动生成一个新的local frame。所以,方法二中的赋值方法是没有问题的。

python入口函数的作用_python之函数中参数的作用域相关推荐

  1. python map函数的作用_Python map()函数介绍及用法

    Python函数式编程之map() Python中map().filter().reduce()这三个都是应用于序列的内置函数. 格式: 1 map(func, seq1[, seq2,-]) 第一个 ...

  2. python匿名函数的作用_Python匿名函数 Lambda表达式作用

    在Python这门优美的编程语言中,支持一种有趣的语法格式(表达式),可以让我们在单行内创建一个最小的函数-python lambda匿名函数. 据说是借鉴了Lisp语言中lambda表达式,它可以使 ...

  3. python map函数的作用_python map函数用法详解

    原博文 2018-05-02 16:24 − python中的map()函数是一个内置的高阶函数,一般用法是map(function, iterable).需要传入一个函数,这个函数可以是内置的,也可 ...

  4. python字符串strip的作用_Python字符串函数strip()原理及用法详解

    Python字符串函数strip()原理及用法详解 strip:用于移除字符串头尾指定的字符(默认为空格)或字符序列.注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符. 语法:str.s ...

  5. 匿名函数php作用,深入理解PHP中的匿名函数

    匿名函数的作用就是扩大函数的使用功能,在PHP(PHP培训 php教程 ) 5.3以前,传递Callback的方式,我们只有俩种选择: ◆字符串的函数名 ◆使用create_function的返回 在 ...

  6. python主函数的作用_python中main函数的用法

    原博文 2020-03-27 20:25 − **什么场景下会有main函数?** 当该python脚本被作为模块(module)引入(import)时,其中的main()函数将不会被执行. **ma ...

  7. python中匿名函数的作用_Python 中的匿名函数,你会用吗

    原标题:Python 中的匿名函数,你会用吗 概念 我们从一个例子引入. 这里有一个元素为非空字符串的列表,按字符串最后一个字母将列表进行排序.如果原列表是 ['abc', 'g', 'def'],则 ...

  8. python map函数的作用_Python的map函数

    map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回. 例如,对于list [1, 2 ...

  9. python中的点的作用_Python基础学习中关键点的作用(三),python,重点,之,函数,3

    函数学习之匿名函数 定义: 匿名函数是指在python中使用lambda所创建函数,称之为匿名函数. 特点: 创建函数不再使用def创建,而是使用lambda关键字创建一个形式主义的函数. 匿名函数的 ...

最新文章

  1. python用jieba进行分词并可视化
  2. Boosting和Bagging: 如何开发一个鲁棒的机器学习算法
  3. 六大“未来式”存储器,谁将脱颖而出?
  4. nginx多站点配置,以及隐藏index.php
  5. boost::hana::basic_tuple用法的测试程序
  6. Ipython\Jupyter数据分析工具
  7. 跨平台、多浏览器页面测试
  8. go写的图片爬虫,支持单页以及列表
  9. java 内部类_我有心上人了,Java内部类
  10. 基于multisim的晶体管放大器设计
  11. LBP—局部二值模式
  12. VMware Workstation中安装系统和VMware tools
  13. usnews美国大学计算机排名2021,2021年USNEWS美国大学计算机
  14. ListView 里面的checkBox,当其中一个被选中,其余全不被选中
  15. 不属于二代计算机网络的实例,计算机网络技术习题
  16. Java实现RPG游戏(mini版pokemon)
  17. android dex 加固,Dex文件加固
  18. 未来教育计算机一本通,未来教育·全国计算机等级考试一本通:一级B(2013年考试专用)...
  19. html设置图片切割,JavaScript html js图片切割系统
  20. 计算机中文输入法教案,输入汉字的学习

热门文章

  1. 苏教版国标本小学语文第一册汉字笔画
  2. 从 class 文件 看 synchronize 锁膨胀过程(偏向锁 轻量级锁 自旋锁 重量级锁)
  3. Android 视频播放器,VideoView播放视频
  4. docker centos ssh
  5. mongoose更新数据,如果这条记录不存在,则直接变为新增
  6. java接口与集合_【总结】Java常用集合接口与集合类
  7. 从底层重学 Java 之 Stream 初探 Gitchat连接
  8. VS2010 C++编译报错LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
  9. linux 修改文件访问权限
  10. java解析json数组对象_JAVA中快速解析JSON对象里包含的JSON数组