Python回顾与整理12:执行环境

0.说明

作为《Python核心编程》核心部分的最后一章,这篇的内容也相当重要。对于高级部分的整理,将采用《Python核心编程》第三版,但是,方式会以之前的完全不一样了。



1.可调用对象

可调用对象即可通过函数操作符“()”来调用的对象,也可以通过函数式编程接口来进行调用,如apply()、filter()、map()和reduce()。Python有4种可调用对象:函数、方法、类和一些类的实例。

(1)函数

Python中有三种不同类型的函数:内建函数(BIF)、用户定义的函数(UDF)和lambda表达式。

  • 内建函数(BIF)

用C/C++写的,编译过后放入Python解释器中,然后把它们作为第一(内建)名称空间的一部分加载进系统,这些函数在__builtin__模块里,并作为__builtins__模块导入到解释器中。

可以使用dir()来列出函数的所有属性,另外从内部机制来看,BIF和内建方法属于相同的类型,所以全长type()调用的结果是:

>>> type(dir)
<type ‘builtin_function_or_method‘>
>>> 
>>> type(‘xpleaf‘.upper)
<type ‘builtin_function_or_method‘>

但是应用于工厂函数,得到的结果是不一样的,虽然这些工厂函数本身是内建函数:

>>> type(int)
<type ‘type‘>

那是因为,本质上这些工厂函数是类。

  • 用户定义的函数(UDF)

用户定义的函数通常是用Python写的,调用type()的结果如下:

>>> def foo():pass
... 
>>> type(foo)
<type ‘function‘>

UDF本身也有很多属性:

>>> dir(foo)
[‘__call__‘, ‘__class__‘, ‘__closure__‘, ‘__code__‘, ‘__defaults__‘, ‘__delattr__‘, ‘__dict__‘, ‘__doc__‘, ‘__format__‘, ‘__get__‘, ‘__getattribute__‘, ‘__globals__‘, ‘__hash__‘, ‘__init__‘, ‘__module__‘, ‘__name__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘func_closure‘, ‘func_code‘, ‘func_defaults‘, ‘func_dict‘, ‘func_doc‘, ‘func_globals‘, ‘func_name‘]
  • lambda表达式(名为“<lambdda>”的函数)

lambda返回一个函数对象,只是lambda表达式没有给命令绑定的代码提供基础结构,所以要通过函数式编程接口来调用,或把它们的引用赋值给一个变量,通过该变量来进行调用,只是需要注意的是,这个变量仅仅是个别名,并不是函数对象的名字。

通过lambda来创建的函数对象除了没有命名外,和用户定义的函数是有相同的属性的,当然,对于__name__属性,值为"<lambda>":

>>> lambdaFunc = lambda x: x*2
>>> lambdaFunc(100)
200
>>> type(lambdaFunc)
<type ‘function‘>
>>> 
>>> lambdaFunc.__name__
‘<lambda>‘
>>> 
>>> foo.__name__
‘foo‘

(2)方法

用户自定义方法是被定义为类的一部分函数,而许多Python数据类型本身也有方法,这些方法被称为内建方法。

  • 内建方法(BIM)

只有内建类型有内建方法,如下:

>>> type([].append)
<type ‘builtin_function_or_method‘>

BIM和BIF两者享有相同的属性,只是不同的是,BIM的__self__属性指向一个Python对象,而BIF指向None:

>>> dir([].append)
[‘__call__‘, ‘__class__‘, ‘__cmp__‘, ‘__delattr__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__name__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__self__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘]
>>> [].append.__self__
[]
>>> dir.__self__
>>> print [].append.__self__
[]
>>> print dir.__self__
None
  • 用户定义的方法(UDM)

所有的UDM都是相同的类型,即“实例方法”,无论该方法是否有绑定的实例:

>>> class C(object):
...     def foo(self):
...             pass
... 
>>> c = C()
>>> 
>>> type(C.foo)
<type ‘instancemethod‘>
>>> type(c.foo)
<type ‘instancemethod‘>

只是访问对象本身会揭示该方法是否绑定:

>>> c.foo
<bound method C.foo of <__main__.C object at 0x7f37a3c45810>>
>>> C.foo
<unbound method C.foo>

(3)类

调用类,其实就是创建一个实例对象。类有默认构造器,但是这个函数什么都不做,可以通过实现__init__()方法来自定义实例化过程。

(4)类的实例

Python给类提供了名为__call__的特别方法,该方法允许程序创建可调用的对象(实例)。默认情况下没有实现该方法,因此实例是不可调用的。

在定义类时覆盖__call__方法即可达到实例可调用的效果,这样,调用实例对象就等同于调用__call__()方法,参数的设置跟类的构造器原理是一样的:

>>> class C(object):
...   def __call__(self, *args):
...     print "I‘m callable! Called with args:\n", args
... 
>>> c = C()
>>> c
<__main__.C object at 0x7f37a3c2d090>
>>> callable(c)    # 实例是可调用的
True
>>> c()    # 调用实例
I‘m callable! Called with args:
()
>>> c(3)    # 传入参数
I‘m callable! Called with args:
(3,)
>>> c(3, ‘no more , no less‘)
I‘m callable! Called with args:
(3, ‘no more , no less‘)
>>> 
>>> c.__call__(3)    # 与c(3)的调用原理是一样的
I‘m callable! Called with args:
(3,)
>>> c.__call__(3, ‘no more, no less‘)
I‘m callable! Called with args:
(3, ‘no more, no less‘)


2.代码对象

每个可调用物的核心都是代码对象,由语句、赋值、表达式和其他可调用物组成。查看一个模块意味着观察一个较大的、包含了模块中所有代码的对象。然后代码可以分成语句、赋值、表达式,以及可调用物。可调用物又可以递归分解到下一层,那儿有自己的代码对象。

一般来说,代码对象可以作为函数或者方法调用的一部分来执行(比如语句或赋值等),也可用exec语句或内建函数eval()来执行。从整体上看,一个Python模块的代码对象是构成该模块的全部代码。

如果要执行Python代码,那么该代码必须先要转换成字节编译的代码(又称字节码)。这才是真正的代码对象。然而,它们不包含任何关于它们执行环境的信息,这便是可调用物存在的原因,它被用来包装一个代码对象并提供额外的信息(如属性等相关信息)。

函数对象仅是代码对象的包装,方法则是给函数对象的包装,只是不同于单纯的代码对象,它们还提供了一些额外的信息。当研究到最底层,便会发现这是一个代码对象。

在函数对象中,有一个func_code属性,其实就是指代码对象:

>>> def foo(): pass
... 
>>> foo.func_code
<code object foo at 0x7f37a5daceb0, file "<stdin>", line 1>
时间: 2024-08-02 01:50:46

Python回顾与整理12:执行环境的相关文章

Python回顾与整理5:映像和集体类型

0.说明 依然是按照前面介绍的几种序列类型的思路来总结字映像类型和集合类型,即先做一个介绍,再讨论可用操作符.工厂函数.内建函数和方法. 1.映射类型:字典 映射类型被称做哈希表,而Python中的字典对象就是哈希类型,即它是按照这样的方式来存储数据:(哈希表的算法)是获取键,对键执行一个叫做哈希函数的操作,并根据计算的结果,选择在数据结构的某个地址中来存储对象的值.任何一个值存储的地址取决于它的键,正因为这种随意性,哈希表中的值是没有顺序的,所以Python中的字典对象中的元素也是无序的. 下

Python回顾与整理3:数字

Python回顾与整理3:数字 0.说明 数字用的次数是否多主要是看需求,如果是做自动化运维平台开发,比如做一个自动监控的系统,那么你肯定需要收集一定量的数据,然后再对这些数据做一定的处理,那么这时候,你就一定需要用得上数字的.当然,我这里所说的要不要用数字,指的是,你是否需要对你的数据做一定的处理. 1.数字简介 数字是不可更改类型,也就是说变更数字的值会生成新的对象. (1)创建数值对象并用其赋值(数字对象) >>> anInt = 1 >>> aComplex =

Python回顾与整理4:序列(字符串/列表/元组)

0.说明 序列其实是Python的某几类数据类型的统称,如字符串,列表和元组,将它们统称为序列,是因为:它们的成员有序排列,并且可以通过下标偏移量访问到它的一个或者几个成员. 总结的思路为:先介绍适用于所有序列类型的操作符和内建函数,然后再分别对这几种序列类型进行介绍. 1.序列 序列类型都有相同的访问模式:它的每一个元素都可以通过指定一个偏移量的方式得到,多个元素通过切片操作的方式得到.而在Python序列中,偏移量的规则如下(假设序列长度为N): (1)标准类型操作符 在<Python回顾与

Python回顾与整理2:Python对象

0.说明 说对象是面向对象编程语言最重要的一部分一点也不为过,没有了"对象",面向对象将无从谈起.Python也是如此,如果无法掌握对象,你很难有大的进步与提升. 1.Python对象 (1)对象特性 Python使用对象模型来存储数据,构造任何类型的值都是一个对象,所有的Python对象都拥有下面的三个特性: 身份:每个对象一唯一身份标识,可使用内建函数id()查看该值(可以认为这个值是该对象的内在地址) 类型:对象的类型决定了对象(可以保存什么类型的值,进行什么样的操作,遵循什么样

Python回顾与整理10:模块

0.说明 模块是用来组织Python代码方法的方法,而包则是用来组织模块的,充分利用好包和模块将有利于开发出结构清晰的大型程序. 1.什么是模块 所谓模块,其实就是一个包含了特定功能代码的.py文件,在这个.py文件中,主要有如下的代码类型: 包含数据成员和方法的类 一组相关但彼此独立的操作函数 全局变量 使用import语句就可以导入一个模块中的相关属性. 2.模块和文件 模块是按照逻辑上来组织Python代码的方法,而体现在物理层面上,它就是一个文件,因此,一个文件被看作是一个独立模块,一个

Python回顾与整理8:错误和异常

0.说明 如果想写出用户体验高的代码,那么就需要考虑到在执行自己写的这段代码中在和用户交互的过程中可能会出现的问题,也就是说,需要对可能出现的异常进行处理,只有做好这些工作,才能写出用户体验好的代码. 1.什么是异常 错误 错误是语法(导致解释器无法解释)或逻辑(也就是代码质量问题)上的,在Python中,当检测到错误时,解释器会指出当前流无法继续执行下去,于是就出现了异常. 异常 程序出现了错误而在正常控制流以外采取的行为. 根据上面的解释,可以理解为,只要解释器检测到程序运行时出现了错误(与

Python回顾与整理9:函数和函数式编程

0.说明 无论在什么编程语言中,函数都不可或缺,充分利用函数的特性,可以大大减少我们程序中的代码量. 1.什么是函数 所谓函数,英文名为function,其实就是表示为实现一定功能的一段代码,显然,如果需要多次实现某一功能时,使用函数就是把重复代码放入其中,既节省空间,又有助于保持一致性(主要是修改代码时). (1)函数vs过程 两者都是可以被调用的实体,过程是简单.没有返回值.特殊的函数.在Python中,过程就是函数,因为解释器会隐匿地返回默认值None. (2)返回值与函数类型 在C语言中

Python回顾与整理6:条件和循环

0.说明 提及的内容为:if.while.for及与他们相搭配的else.elif.break.continue和pass语句. 1.if语句 语法 if expression:         expr_true_suite 其中对于expression部分可以使用逻辑连接词an.or和not来实现多重判断条件. 单一语句的代码块 即如果只有一个语句执行时,可以写成下面这样: if True: print 'OK' 但还是建议写在不同的行. 2.else语句 语法 if expression:

Python基础学习代码之执行环境

class C(object):     def __call__(self, *args, **kwargs):         print "I'm callable! called with args:\n",args c = C() c('a',1) single_code = compile("print 'hello,world!'",'','single') exec(single_code) eval_code = compile('100*3','