执行环境 - QLGQ/learning-python GitHub Wiki

可调用对象

许多的Python对象都是我们所说的可调用的,即是任何能通过函数操作符“()”来调用的对象。要调用可调用对象,函数操作符得紧跟在可调用对象之后。比方说,用“foo()”来调用函数“foo”。可调用对象可以通过函数式编程接口来进行调用,如apply()、filter()、map()、reduce()。Python有4种可调用对象:函数、方法、类以及一些类的实例。记住这些对象的任何引用或者别名都是可调用的。

函数

Python有三种不同类型函数对象。

内建函数(BIF)

内建函数(Built-in Function, BIF)是用C/C++写的,编译过后放入Python解释器,然后把它们作为第一(内建)名称空间的一部分加载进系统。这些函数在__builtin__模块里,并作为__builtins__模块导入到解释器中。
BIF有基础类型属性,其中一些独特的属性列在下表中。
内建函数属性

属性 描述
bif.__doc__ 文档字符串(或None)
bif.__name__ 字符串类型的文档名字
bif.__self__ 设置为None(保留给内建方法)
bif.__module__ 存放bif定义的模块名字(或None)

你可以用dir()列出函数所有的属性:

dir(type)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__weakrefoffset__', 'mro']

从内部机制来看,因为BIF和内建方法(BM)属于相同的类型,所以对BIF或者BIM调用type()的结果是:

type(dir)
<type 'builtin_function_or_method'>

注意这不能应用于工厂函数,因为type()正好会返回产生对象的类型:

type(int)
<type 'type'>
type(type)
<type 'type'>

用户定义的函数(UDF)

用户定义的函数(User-Defined Function, UDF)通常是用Python写的,定义在模块的最高级,因此会作为全局名称空间的一部分(一旦创建好内建名称空间)装载到系统中。如同上面的BIF,UDF也有许多的属性。UDF最让人感兴趣和最特殊的属性都列在下面的表中。
用户自定义函数属性

属性 描述
udf.__doc__ 文档字符串(也可以用udf.func_doc)
udf.__name__ 字符串类型的函数名字(也可以用udf.func._name)
udf.func_code 字节编译的代码对象
udf.func_defaults 默认的参数元组
udf.func_globals 全局名字空间字典,和从函数内部调用globals(x)一样
udf.func_dict 函数属性的名字空间
udf.func_doc 见上面的udf.__doc__
udf.func_name 见上面的udf.__name__
udf.func_closure 包含了自由变量的引用的单元对象元组(自由变量在UDF中使用,但在别处定义)

从内部机制来看,用户定义的函数是“函数”类型的,如在下面的例子中用type()表明的一样:

def foo(): pass
type(foo)
<type 'function'>

lambda表达式(名为“<lambda>”的函数)

lambda表达式和用户自定义的函数相比,略有不同。虽然它们也是返回一个函数对象,但是lambda表达式不是用def语句创建的,而是用lambda关键字。
因为lambda表达式没有给命名绑定的代码提供基础结构,所以要通过函数式编程接口来调用,或把它们的引用赋值给一个变量,然后就可以直接调用或者再通过函数来调用。变量仅是个别名,并不是函数对象的名字。
通过lambda来创建函数的对象除了没有命名之外,享有和用户自定义函数相同的属性;__name__或者func_name属性给定为字符串“<lambda>”。

lambdaFunc = lambda x: x * 2
type(lambdaFunc)
<type 'function'>
lambdaFunc.__name__
'<lambda>'

方法

用户自定义方法是被定义为类的一部分函数。许多Python数据类型,比如列表和字典,也有方法,这些被称为内建方法。为了进一步说明“所有权”的类型,方法通过对象的名字和句点属性标识进行命名的。

内建方法(BIM)

只有内建类型(built-in type, BIT)有内建方法(built-in Method, BIM)。对于内建方法,type()工厂函数给出了和BIF()相同的输出。

<type 'builtin_function_or_method'>

此外,BIM和BIF两者也都享有相同的属性。不同之处在于BIM的__self__属性指向一个Python对象,而BIF指向None。

内建方法属性

属性 描述
bim.__doc__ 文档字符串
bim.__name__ 字符串的函数名字
bim.__self__ 绑定的对象

用户定义的方法(UDM)

UDM(User-defined method,用户定义的方法)包含在类定义之中,只是拥有标准函数的包装,仅有定义它们的类可以使用。如果没有在子类定义中被覆盖掉,也可以通过子类实例来调用它们。UDM与类对象是关联的(非绑定方法),但是只能通过类的实例来调用(绑定方法)。无论UDM是否绑定,所有的UDM都是相同的类型——“实例方法”

class C(object):    # 定义类
    def foo(self): pass    # 定义UDM

c = C()    # 实例化
type(C)    # 类的类别
<type 'type'>
type(c)    # 实力的类别
<class '__main__.C'>
type(C.foo)    # 非绑定方法的类别
<type 'instancemethod'>
type(c.foo)    # 绑定方法的类别
<type 'instancemethod'>

访问对象本身将会揭示你正在引用一个绑定方法还是非绑定方法。 绑定的方法揭示了方法绑定到哪一个实例。

C.foo    # 非绑定方法对象
<unbound method C.foo>
c.foo    # 绑定方法对象
<bound method C.foo of <__main__.C object at 0x7fba15123050>>
c    # foo()实例被绑定到......
<__main__.C object at 0x7fba15123050>

下表展示了UDM的属性
用户自定义方法属性

UDM属性 描述
udm.__doc__ 文档字符串(与udm.im_func.__doc__相同)
udm.__name__ 字符串类型的方法名字(与umd.im_func.__name__相同)
udm.__module__ 定义udm的模块的名字(或None)
udm.im_class 方法相关联的类(对于绑定的方法;如果是非绑定的,那么为要求udm的类)
udm.im_func 方法的函数对象
udm.im_self 如果绑定的话为相关联的实例,如果非绑定为None

我们可以利用类的可调用性来创建实例。“调用”类的结果便是创建了实例,即大家所知道的实例化。类有默认构造器,该函数什么都不做,基本上只有一个pass语句,程序员可以通过实现__init__()方法,来自定义实例化过程。实例化调用的任何参数都会传入到构造器里。

class C(object):
    def __init__(self, *args):
        print 'Instantiated with these arguments:\n', args

c1 = C()    # invoking class to instantiate c1
Instantiated with these arguments:
()

c2 = C('The number of the counting shall be', 3)
Instantiated with these arguments:
('The number of the counting shall be', 3)

类的实例

Python给类提供了名为__call__的特别方法,该方法允许程序员创建可调用的对象(实例)。默认情况下,__call__()方法是没有实现的,这意味着大多数实例都是不可调用的。然而,如果在类定义中覆盖了这个方法,那么这个类的实例就成为可调用的了。调用这样的实例对象等同于调用__call__()方法。
自然地,任何在实例调用中给出的参数都会被传入到__call__()中。那么foo()就和foo.__call__(foo)的效果相同,这里foo也作为参数出现,因为是对自己的引用,实例将自动成为每次方法调用的第一个参数。如果__call__()有参数,比如(self, arg),那么foo(arg)就和调用foo.__call__(foo, arg)一样。

class C(object):
    def __call__(self, *args):
        print "I'm callable! Called with args:\n", args

c = C()    # 实例化
c    # 我们的实例
<__main__.C object at 0x7fba132e1d50>
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')

可执行的对象声明和内建函数

Python提供了大量的BIF来支持可调用/可执行对象,其中包括exec语句。这些函数帮助程序员执行代码对象,也可以用内建函数compile()来生成代码对象。
可执行的对象声明和内建函数

内建函数和语句 描述
callable(obj) 如果obj可调用,返回True,否则返回False
compile(string, file, type) 从type类型中创建代码对象,file是代码存放的地方(通常设为"")
eval(obj, globals=globals(), locals=locals()) 对obj求值,obj是已编译为代码对象的表达式,或是一个字符串表达式,可以给出全局或者/和局部的名称空间
exec obj 执行obj,单一的Python语句或者语句的集合,也就是说格式是代码对象或者字符串,obj也可以是一个文件对象(已经打开的有效Python脚本中)
input(prompt='') 等同于eval(raw_input(prompt=''))

callable()

callable()是一个布尔函数,确定一个对象是否可以通过函数操作符(())来调用。如果函数可调用便返回True,否则便是False。

compile()

compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用exec语句或者内建函数eval()来执行这些对象或者对它们进行求值。exec和eval()都可以执行字符串格式的Python代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。compile()函数提供了一次性字节代码预编译,以后每次调用的时候,都不用编译了。 compile的三个参数都是必需的。第一个参数代表了要编译的Python代码。第二个字符串虽是必需的,但通常被置为空串,该参数代表了存放代码对象的文件的名字(字符串类型)。compile的通常用法是动态生成字符串形式的Python代码,然后生成一个代码对象——代码明显没有存放在任何文件。最后的参数是字符串,它用来表明代码对象的类型,有三个可能的值:

  • 'eval' 可求值的表达式[和eval()一起使用]
  • 'single' 单一执行语句[和exec一起使用]
  • 'exec' 可执行语句组[和exec一起使用]
eval_code = compile('100+200', '', 'eval')
eval(eval_code)
300

single_code = compile('print "Hello world!"', '', 'single')
single_code
<code object <module> at 0x7f70106d9eb0, file "", line 1>
exec single_code
Hello world!

exec_code = compile("""
    req = input('Count how many numbers? ')
    for eachNum in range(req):
        print eachNum
""", '', 'exec')
exec exec_code
Count how many numbers? 6
0
1
2
3
4
5

eval()

eval()对表达式求值,后者可以为字符串或内建函数compile()创建的预编译代码对象。这是eval()的第一个参数,是你想要执行的对象。第二个和第三个参数都是可选的,分别代表了全局和局部名称空间中的对象。如果给出这两个参数,globals必须是个字典,locals可以是任意的映射对象,比如,一个实现了getitem()方法的对象。如果都没给出这两个参数,分别默认为globals()和locals()返回的对象,如果只传入了一个全局字典,那么该字典也作为locals传入。

eval('100 + 200 ')
300

exec

和eval()相似,exec语句执行代码对象或字符串形式的Python代码。exec语句只接受一个参数,通用语法为exec obj。被执行的对象(obj)可以只是原始的字符串,比如单一语句或是语句组,它们也可以预编译成一个代码对象(分别用“single”和“exec”参数)。

exec  """
    x = 0
    print 'x is currently: ', x
    while x < 5:
        x += 1
        print 'incrementing x to: ', x
    """

x is currently: 0
incrementing x to: 1
incrementing x to: 2
incrementing x to: 3
incrementing x to: 4
incrementing x to: 5

最后,exec还可以接受有效的Python文件对象。如果我们用上面的多行代码创建一个叫xcount.py的文件,那么也可以用下面的方法执行相同的代码:

f = open('xcount.py')    # 打开文件
exec f
x is currently: 0
incrementing x to: 1
incrementing x to: 2
incrementing x to: 3
incrementing x to: 4
incrementing x to: 5

input

内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input())。类似于raw_input(),input()有一个可选的参数,该参数代表了给用户的字符串提示。如果不给定参数的话,该字符串默认为空串。
从功能上看,input()不同于raw_input(),因为raw_input()总是以字符串的形式,逐字地返回用户的输入。input()履行相同的任务,而且,它还把输入作为Python表达式进行求值。这意味着input()返回的数据是对输入表达式求值的结果:一个Python对象。

aString = raw_input('Enter a list: ')
Enter a list: [123, 'xyz', 45.67]
aString
"[123, 'xyz', 45.67]"
type(aString)
<type 'str'>

aList = input('Enter a list: ')
Enter a list: [123, 'xyz', 45.67]
aList
[123, 'xyz', 45.67]
type(aList)
<type 'list'>

os模块

os.system()

该函数接收字符串形式的系统命令并执行它。当执行命令的时候,Python的运行是挂起的。当我们的执行完成之后,将会以system()的返回值形式给出退出状态,Python的执行也会继续。通常的约定是利用退出状态,0表示成功,非0表示其他类型的错误。

import os
result = os.system('uname -a')
Linux qiang 3.19.0-68-generic #76~14.04.1-Ubuntu SMP Fri Aug 12 11:46:25 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
result
0

os.popen()

popen()函数是文件对象和system()函数的结合。在上面其中一个使用system()的例子中,我们调用了unix程序uname来给我们提供机器和使用的操作系统的相关信息。该命令产生了一行输出,并直接写到屏幕上。如果想要把该字符串读入变量中并执行内部操作或者把它存储到日志文件中,我们可以使用popen()。

import os
f = os.popen('uname -a')
data = f.readline()
f.close()
print data
Linux qiang 3.19.0-68-generic #76~14.04.1-Ubuntu SMP Fri Aug 12 11:46:25 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

如你所见,popen()返回一个类文件对象;注意readline(),往往保留输入文本行尾的newline字符。

⚠️ **GitHub.com Fallback** ⚠️