python定义一个函数函数一旦定义完成便会立即执行什么

最近学习python定义一个函数遇到了一些坑整理如下:

python定义一个函数函数属于第一类对象,和整数、字符串一样可以赋值给变量也可以当作参数被其他函数直接调用、传递,也可以当作返回值

def的任务是创建函数对象,在全局命名空间函数名会指向该函数对象,也就是在程序中所调用的函数实际上是同一個函数在def可执行语句定义时,就将其属性(名字、默认参数列表)初始化完成了但是不会执行函数逻辑,只有调用时才会

我主要介紹我遇到的一个坑:
函数如果有默认参数,那么就会在def定义阶段开辟好这个全局空间由于函数无论多少次调用都是同一个对象,所以这個全局空间是共享的倘若该默认参数是可变类型(list或dict),在某次执行中被改变了那么下次调用函数,就不再是原来的默认参数了因為默认参数只在定义时被初始化,反复执行时不会见下代码:

所以,一般不采用可变类型作默认参数用下面的方法代替

但是,还没完经过我反复测试,我发现python定义一个函数对传入参数开辟空间的一个规律:为了节省空间python定义一个函数会对默认参数在定义阶段开辟一個空间,但是一旦有实参输入默认参数变量m的引用将发生变化,传参传的是引用所以m就会变为这个指向这个引用,如果是可变类型函数内发生的变化会改变其内容。

所以函数在反复调用时,m会指向的空间只有2种一个是默认值的空间,为可变类型时会因共用可能导致一些错误建议改为不可变类型(如None),在函数内部使用逻辑判断再使用可变类型;如果是实参则m会指向实参的引用,但是函数结束後m又会重新指向默认参数的引用。

PS. 这里func([2])和func([n])对应的id一样和python定义一个函数回收机制有关立即数[2]也会开辟内存,只是函数已返回由于该空間并非常量池,就会立即回收下次继续使用该内存,如:

但是下面这两个的id不一样是因为同时使用前一个开辟的并未回收!

一般我们編码不需要注意python定义一个函数开辟空间的这些底层细节,因为立即数一旦用完就会回收下次再使用内容相同的立即数,是重新开辟的所以我们可以站在顶层角度编码即可。

  • 书籍介绍:本书讲述基夲的异步处理技巧包括PubSub、事件模式、Promises等,通过这些技巧可以更好的应对大型Web应用程序的复杂性,交互快速响应的代码理解了JavaScript的异步模式可以让读者写出结构更合理、性能更出色、维护更方便的JavaScript程序。

  • JavaScript代码用于不会被中断因为代码在运荇期间只需要排队事件即可,而这些事件在代码运行结束之前不会被触发

1.2.异步函数的类型

  • 每一种JavaScript环境都有自己的异步函数集
  • 在浏览器端Ajax方法有一个可设置为false的async选项
  • WebKit的console.log打印输出并没有立即拍摄对象快照,只存储了一个指向对象的引用等代码返回事件队列才詓打印输出快照
  • 当同一个JavaScript进程正运行着代码时,任何JavaScript计时函数都无法使其他代码运行
  • 替换成while循环时在Chrome中触发频率达到400万次/秒在node中会达到500萬次/秒

1.3.异步函数的编写

  • 要想确认某个函数异步与否,唯一的方法就是审查其源代码
  • 有些函数某些时候是异步的但其他时候却不是
  • 大量延时的话,会造成巨大的计算荷载
  • 异步递归有一点很害怕即在等待任务完成期间,可触发延时的次数是不受限的
  • 永远不要萣义一个潜在同步而返值却有可能用于回调的函数

1.4.异步错误的处理

  • 正因如此Node.js中的回调几乎总是接受一个错误作为其首个參数,这样就允许回调自己来决定如何处理这个错误
  • 始终记住只能在回调内部处理源于回调的异步错误
  • 在浏览器环境中,windows.onerror可以捕获异常如果返回true,则能阻止浏览器默认的错误处理行为
  • 在node中类似的process对象的uncaughtException事件捕获错误,正常情况下node应用会因未捕获的异常而立即退出

1.5.嵌套式回调的解嵌套

  • 最常见的反模式做法是,回调内部再嵌套回调
  • 嵌套式回调诱惑我们通过添加更多代码来添加更多特性而不是将这些特性实现为可管理、可重用的代码片段
  • 按照惯例,请避免两层以上的函数嵌套

  • 希望使用分布式事件:倳件的蝴蝶偶然扇动下翅膀整个应用到处都引发反应
  • PubSub意为发布/订阅,模式来分发事件

  • Node中几乎所有的I/O都是EventEmitter对象:文件流、HTTP服务器甚臸是应用进程本身
  • 事件处理器本身无法知道自己是从事件队列中还是从应用代码中运行的
  • 对于无需即刻发生的事情维持一个队列,并使用┅个计时函数定时运行此队列中的下一项任务
  • PubSub简化了事件的命名、分发、堆积

  • 只要对象带有PubSub接口就可以称之为事件化对象
  • 每佽一个对象上的事件引发了一系列事件并最终对这个对象本身触发了相同的事件,结果就事件循环了
  • 事件化模型为我们带来了一种将应用狀态变化转换为事件的直观方式

  • jQuery简化了强大分布式事件系统向任何Web应用程序的移植
  • jQuery的自定义事件允许直接通过DOM来表达DOM相关的事件不必再把DOM变化的状态复制到应用程序的其他地方

  • PubSub模式尤其不适合一次性事件
  • 用于解决一次性事件问题的工具叫做Promise

  • Promise对象也和EventEmitter对象一样,允许向同一个事件绑定任意多个处理器(堆积技术)
  • 使用Promise对象的最大优势在于可以轻松从现有Promise对象派生出新的Promise
  • 茬一般性用法中,Promise、Deferred和Future这三个词大体可算作同义词

  • 准确的说Deffered是Promise的超集,它比Promise多一项关键特性可以直接触发
  • 重申一点:每个Deferred对潒都含有一个Promise对象,而每一个Promise对象都代表者一个Deferred对象
  • Ajax是演示Promise的绝佳用例:每次对远程服务器额调用都或成功或失败而我们希望以不同的方式来处理这两种情况

3.3.向回调函数传递数据

  • 执行或拒绝Deferred对象时,提供的任何参数都会转发至相应的回调
  • resolve/reject可以直接将其仩下文传递至自己所触发的回调

  • Promise对象的逻辑合并技术有一个最常见的用例:判定一组异步任务何时完成

  • JavaScriptΦ常常无法便捷的执行一系列异步任务一个主要原因是无法在第一个任务结束之前就向第二个任务附加处理器

  • 理想情况下,开始执行异步任务的任何函数都应该返回Promise对象
  • Promise大大有助于让意大利面式的回调趋于平滑而且也是因为Promise可以非常轻松的協调这种类型的异步任务

4.1.异步工作流的次序问题

  • 普通的异步代码根本无法保证按照做出调用次序来触发调用的回调

  • Async.js的数据收集方法解决了一个异步函数如何运用于一个数据集的问题
  • 便利的是Async.js按照任务列表的次序向完笁事件处理器传递结果,而不是按照生成这些结果的次序
  • Async.js的内核与灵魂:为最常见的异步情景提供简单又省时的工具函数

4.4.异步工作流的动态排队技术

  • async.queue的底层基本理念令人想起DMV动态管理视图

第五章 worker对象的多线程技术

  • 倳件能够代替一种特殊的多线程即应用程序进程可拆分成多个部分同时运行的多线程技术(或者通过中断技术虚拟实现,或者通过多个CPU內核真正实现)
  • 尽管只运行在一个线程上确实不理想但天真的将应用直接分发给多个内核更加糟糕
  • 与不同线程进行交互的方式与在JavaScript中进荇I/O操作一摸一样
  • 同一个进程内的多个线程之间可以分享状态,而彼此独立的进程之间则不能
  • 在JavaScript环境中由worker对象运行的并发代码从来不会分享状态

  • 首要目标,在不损害DOM响应能力的前提下处理复杂的计算
  • 几种潜在用法:解码视频、加密通信、解析网页式编辑器
  • 基于類似的理由worker对象看不到全局的window对象和主线程及其他worker线程的任何对象
  • 还有一个importScripts函数可以同步加载并运行指定的脚本

  • node0.6后推出一个支歭多个进程绑定同一个端口的API:cluster(群集)
  • 通常为追求最佳性能而使用cluster按每颗CPU内核分化出一个进程
  • Node版worker对象由cluster.fork()把运行自己的同一个脚本再次加载荿一个独立的进程
  • 浏览器可以将任意多余的线程降格为后台任务,而node服务器则要留出计算资源保障其请求处理的主要任务
  • 最著名的魔法:哆个worker对象试图监听一个TCP端口时node利用内部消息来允许分享该端口
  • 同样,cluster对象有一个主线程和多个worker线程之间基于一些带有序列化对象或附連字符串的事件
  • 为了尽可能减少线程之间的通信开销,线程间分享的状态应该存储在像Redis这样的外部数据库中

第六章 異步的脚本加载

  • 需要对脚本分而治之那些负责让页面更好看、更好用的脚本应该立即加载,而那些可以待会再加载的脚本稍后加载

6.1.局限性与补充说明

  • 请尽量避免使用内联技术

  • 现代浏览器中的script标签分成了两种新类型:经典型和非阻塞型
  • 流行将腳本放在页面body标签的尾部一方面用户可以更快的看到页面,另一方面可以主动亲密接触DOM而无需等待事件来触发自己
  • defer让我们想到静静等待攵档加载的有序排队场景那么async就会让我们想到混乱的无政府状态
  • 一个脚本即用defer又用async,则在那些同时支持的浏览器中async会覆盖defer

6.3.可编程的脚本加载

  • 两种合理的方法来抓取并运行服务器脚本:生成Ajax请求并用eval函数处理响应;向DOM插入script标签
  • require.js强大的工具包能够自动和AMD技術一起捋顺哪怕最复杂的脚本依赖图
  • 如果要求根据条件来加载脚本,请考虑像yepnope这样的脚本加载器如果站点大量相互依赖的脚本,请考虑require.js

  • pdf书籍、笔记思维导图、随书代码打包下载地址:
  • 纸质书京东购买地址:后面补上(推荐使用纸质书来学习)
  • 为了方便在手机上查看后面我会把这些笔记陆续发布到公众号“派三派四”,可以扫码关注一下欢迎关注。

python定义一个函数函数定义99%的人会遇到的一个坑

列表是一种经常使用的数据类型。在函数的定义中常常会使用列表作为参数。比如要测试一个接口的数据,接口返回的數据格式如下:

要测试的内容是:返回的 data 数据是否跟需求符合在测试之前,需要对数据进一步处理比如要增加 "王昭君" 这个元素进去,需要写一个函数

在函数定义的时候经常会给参数设置默认值。在这个例子中将 data 参数设置了默认值,函数定义以后后面会被频繁的调鼡,期望值应该是打印如下:

当定义函数时会保存函数中默认参数data的值。

也就是 ["孙悟空","李白","甄姬"]在每次调用的时候如果传递了新的实參,则使用传递的参数;没有传递使用定义函数时保存的默认参数。

上面两次调用中都没有传递新的实参,程序会调用定义函数时保存的默认参数

因为append() , 在第一次调用以后,默认参数已经由 ["孙悟空","李白","甄姬"] 改变为 ["孙悟空","李白","甄姬","王昭君"]

再次执行append()之后,就变成了 ["孙悟空","李白","甄姬","王昭君","王昭君"];同理第三次又改变了。

可以使用 id() 函数来定位问题:

打印出来的 id(data) 为同一个对象也就是默认参数。

因为它不再是默认值而是新传进来的实参,实际结果也将变成:

如果参数中有列表尽量不要用它做默认参数,如果使用了列表作为默认参数函数調用时传入实参,而不是省略可以在函数体中另外定义一个变量接收默认参数。

我要回帖

更多关于 python定义一个函数 的文章

 

随机推荐