为什么80%的码农都做不了架构师?>>>   

本章主题

  • 可调用对象

  • 代码对象

  • 语句和内置函数

  • 执行其他程序

  • 终止执行

  • 各类操作系统接口

  • 相关模块

python 中有多种运行外部程序的方法,比如,运行操作系统命令或另外的python 脚本,或执行一个磁盘上的文件,或通过网络来运行文件。有些特定的执行场景包括:

  • 在当前脚本继续运行

  • 创建和管理子进程

  • 执行外部命令或程序

  • 执行需要输入的命令

  • 通过网络来调用命令

  • 执行命令来创建需要处理的输出

  • 执行其他的 Python 脚本

  • 执行一系列动态生成的 Python 语句

  • 导入 Python 模块 (和执行它顶层的代码)

python 中,内建和外部模块都可以提供上述各种功能。程序员得根据实现的需要,从这些模块中选择合适的处理方法。本章将对python 执行环境进行全面的描述,但不会涉及如何启动python解释器和不同的命令行选项。读者可以从第二章中查阅到相关信息。

python 执行环境之旅从可调用对象开始,接着是代码对象,然后去看看什么样的python语句和内建函数适合支持我们需要的功能。执行其他程序的能力不仅大大增强了python 脚本的威力,也节约了资源,因为重复实现这些代码肯定是不合逻辑的,更是浪费时间和人力。python 给当前脚本环境提供了许多执行程序或者外部命令的机制,我们将介绍下最普遍的几个命令。接下来,我们对python 的受限执行环境作一个简短的概况,最后,介绍下各种终止执行的方法(而不是让程序正常完成)。就从可调用对象开始我们的旅程吧。

14.1 可调用对象

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

14.1.1 函数

python 有3 种不同类型函数对象:内建函数、用户自定义函数、方法

内建函数(BIFs)

BIF 是用c/c++写的,编译过后放入python 解释器,然后把它们作为第一(内建)名字空间的一部分加载进系统。如前面章节所提到的,这些函数在_bulitin_模块里,并作为__builtins__模块导入到解释器中。

表14.1 内建函数属性

BIF 有基础类型属性,其中一些独特的属性已列在表14.1 中你可以用dir()列出函数的所有属性:

>>> dir(type)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__','__getattribute__', '__hash__', '__init__', '__module__','__name__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__self__', '__setattr__', '__str__']

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

>>> type(dir)
<type 'builtin_function_or_method'>

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

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

用户定义的函数(UDF)

UDF(User-Defined Function)通常是用python 写的,定义在模块的最高级,因此会作为全局名字空间的一部分(一旦创建好内建名字空间)装载到系统中。函数也可在其他的函数体内定义,并且由于在2.2 中嵌套作用域的改进,可以对多重嵌套作用域中的属性进行访问。可以用func_closure 属性来钩住在其他地方定义的属性。

表14.2 用户自定义函数属性

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

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

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

通过lambda 来创建函数对象除了没有命名之外,享有和用户自定义函数相同的属性;__name__或者func_name 属性给定为字符串"<lambda>"。使用type()工厂函数,lambda 表达式返回和用户自定义函数相同的函数对象:

>>> lambdaFunc = lambda x: x * 2
>>> lambdaFunc(100)
200
>>> type(lambdaFunc)
<type 'function'>

在上面的例子中,我们将表达式赋值给一个别名。我们也可以直接在一个lambda 表达式上调用type():

>>> type(lambda:1)
<type 'function'>

我们快速的来看看UDF 名字,使用上面的lambdaFunc 和先前小节中的foo():

>>> foo.__name__
'foo'
>>> lambdaFunc.__name__
'<lambda>'

从11.9 小节中我们可以看到,一旦函数声明以后(且函数对象可用),程序员也可以自定义函数属性。所有的新属性变成udf.__dict__对象的一部分。在本章的稍后内容中,将讨论获取含有python 代码的字符串并执行该代码。到了本章最后,会有一个组合例子,着重描写函数属性和python 代码(字符串)的动态求值和执行语句。

14.1.2 方法

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

内建方法(BIMs)

在前面的小节中,我们讨论了内建方法与内建函数的类似之处。只有内建类型(BIT)有BIM。对于内建方法,type()工厂函数给出了和BIF 相同的输出--注意,我们是如何提供一个内建对象来访问BIM:

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

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

对于类和实例,都能以该对象为参数,通过内建函数dir()来获得他们的数据和方法属性。这也可以用在BIM 上:

>>> dir([].append)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__module__',
'__name__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__self__', '__setattr__', '__str__']

不用多久就会发现,从功能上看,用实际的对象去访问其方法并不是非常有用,如最后的例子。由于没有引用来保存这个对象,所以它立即被垃圾回收了。你处理这种访问的类型唯一的用处就是显示BIT 有什么方法。

用户定义的方法(UDM)

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

>>> class C(object): # define class # 定义类
... def foo(self): pass # define UDM # 定义UDM
...
>>> c = C() # instantiation # 实例化
>>> type(C) # type of class # 类的类别
<type 'type'>
>>> type(c) # type of instance # 实例的类别
<class '__main__.C'>
>>> type(C.foo) # type of unbound method # 非绑定方法的类别
<type 'instancemethod'>
>>> type(c.foo) # type of bound method # 绑定方法的类别
<type 'instancemethod'>

表11.4 中展示了UDM 的属性。访问对象本身会揭示正在引用的是绑定方法还是非绑定方法。eg,绑定的方法揭示了方法绑定到哪一个实例:

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

14.1.3 类

可以利用类的可调用性来创建实例。“调用”类的结果便是创建了实例。

>>> 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)

一个新的问题是如何让实例能够被调用。

14.1.4 类的实例

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() # instantiation # 实例化
>>> c # our instance # 我们的实例
<__main__.C instance at 0x00B42DD0>
>>> callable(c) # instance is callable #实例是可调用的
True
>>> c() # instance invoked # 调用实例
I'm callable! Called with arguments:
()
>>> c(3) # invoked with 1 arg # 呼叫的时候给出一个参数
I'm callable! Called with arguments:
(3,)
>>> c(3, 'no more, no less') # invoked with 2 args # 呼叫的时候给出两个参数
I'm callable! Called with arguments:
(3, 'no more, no less')

记住只有定义类的时候实现了__call__方法,类的实例才能成为可调用的。

14.2 代码对象

可调用的对象是python 执行环境里最重要的部分,却只是冰山一角。python 语句,赋值,表达式,甚至还有模块构成了更宏大的场面。这些可执行对象无法像可调用物那样被调用。更确切地说,这些对象只是构成可执行代码块的拼图的很小一部分,而这些代码块被称为代码对象。每个可调用物的核心都是代码对象,由语句,赋值,表达式,以及其他可调用物组成。查看一个模块意味着观察一个较大的、包含了模块中所有代码的对象。然后代码可以分成语句,赋值,表达式,以及可调用物。可调用物又可以递归分解到下一层,那儿有自己的代码对象。一般说来,代码对象可以作为函数或者方法调用的一部分来执行,也可用exec 语句或内建函数eval()来执行。从整体上看,一个python 模块的代码对象是构成该模块的全部代码。如果要执行python 代码,那么该代码必须先要转换成字节编译的代码(又称字节码)。这才是真正的代码对象。然而,代码对象不包含任何关于代码对象执行环境的信息,这便是可调用对象存在的原因,可调用对象被用来包装一个代码对象并提供额外的信息。还记得前面的小节中UDF(用户自定义函数) 的udf.func_code 属性吗?那就是代码对象。UDM (用户自定义方法)的udm.im_func 函数对象又是怎么一回事呢?因为那也是一个函数对象,所以他同样有它自己的udm.im_func.func_code 代码对象。这样的话,你会发现,函数对象仅是代码对象的包装,方法则是给函数对象的包装。你可以到处看看。当研究到最底层,你会发现便是一个代码对象。

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

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

14.3.1 callable()

callable()是一个布尔函数,确定一个对象是否可以通过函数操作符(())来调用。如果函数可调用便返回True,否则便是False,这里有些对象及其对应的callable 返回值:

>>> callable(dir) # built-in function # 内建函数
True
>>> callable(1) # integer #整数
False
>>> def foo(): pass
...
>>> callable(foo) # user-defined function # 用户自定义函数
True
>>> callable('bar') # string #字符串
False
>>> class C(object): pass
...
>>> callable(C) # class #类
True

14.3.2 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 ? at 120998, file "", line 0>
>>> 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

在最后的例子中,我们第一次看到input()。一直以来,我们都是从raw_input()中读取输入的。内建函数input()是我们将在本章稍后讨论的一个快捷函数。

14.3.3 eval()

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

>>> eval('932')
932
>>> int('932')
932

在这种情况下,eval()和int()都返回相同的结果:整数932。然而,它们采用的方式却不尽相同。内建函数eval()接收引号内的字符串并把它作为python 表达式进行求值。内建函数int()接收代表整数的字符串并把它转换为整数。这只有在该字符串只由字符串932 组成的时候才会成功,而该字符串作为表达式返回值932,932 也是字符串”932”所代表的整数。当我们用纯字符串表达式的时候,两者便不再相同了:

>>> eval('100 + 200')
300
>>> int('100 + 200') Traceback (innermost last):
File "<stdin>", line 1, in ?
ValueError: invalid literal for int(): 100 + 200

在这种情况下,eval()接收一个字符串并把"100+200"作为表达式求值,当进行整数加法后,给出返回值300。而对int()的调用失败了,因为字符串参数不是能代表整数的字符串, 因为在字符串中有非法的文字,即,空格以及“+”字符。可以这样理解eval()函数的工作方式:对表达式两端的引号视而不见。

14.3.4 exec

和eval()相似,exec 语句执行代码对象或字符串形式的python 代码。类似地,用compile()预编译重复代码有助于改善性能,因为在调用时不必经过字节编译处理。exec 语句只接受一个参数,下面便是它的通用语法:

exec obj

被执行的对象(obj)可以只是原始的字符串,比如单一语句或是语句组,它们也可以预编译成一个代码对象(分别用'single'和'exec"参数)。下面的例子中,多个语句作为一个字符串发送给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') # open the file
>>> exec f # execute the file
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 f #尝试再一次执行
>>> #哦,失败了....为什么?

注意一旦执行完毕,继续对exec 的调用就会失败。并不是真正的失败,只是不再做任何事。事实上,exec 已从文件中读取了全部的数据且停留在文件末尾(EOF)。当用相同文件对象对exec 进行调用的时候,便没有可以执行的代码了,所以exec 什么都不做,如同上面看见的行为。我们如何知道它在EOF 呢?

用文件对象的tell()方法来告诉我们处于文件的何处,然后用os.path.getsize()来告诉我们xcount.py 脚本有多大。这样你就会发现,两个数字完全一样:

>>> f.tell() # where are we in the file? # 我们在文件的什么地方?
116
>>> f.close() # close the file # 关闭文件
>>> from os.path import getsize
>>> getsize('xcount.py') # what is the file size? # 文件有多大?
116

如果想在不关闭和重新打开文件的情况下再次运行它,可以用seek()到文件最开头并再次调用exec 了。比如,假定我们还没有调用f.close(),那么我们可以这样做:

>>> f.seek(0) # rewind to beginning 倒会文件开头
>>> 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
>>> f.close()

14.3.5 input()

内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input())。类似于raw_input(),input()有一个可选的参数,该参数代表了给用户的字符串提示。如果不给定参数的话,该字符串默认为空串。

从功能上看,input 不同于raw_input(),因为raw_input()总是以字符串的形式,逐字地返回用户的输入。input()履行相同的的任务;而且,它还把输入作为python 表达式进行求值。这意味着input()返回的数据是对输入表达式求值的结果:一个python 对象。

下面的例子会让人更加清楚:当用户输入一个列表时,raw_input()返回一个列表的字符串描绘,而input()返回实际的列表:

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

上面用raw_input()运行。正如你看见的,每样东西都是字符串。现在来看看当用input()的时候会发生什么:

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

虽然用户输入字符串,但是input()把输入作为python 对象来求值并返回表达式的结果。

14.3.6 使用Python 在运行时生成和执行Python 代码

在这个小节中,我们将看到两个python 脚本的例子,这两个例子在运行时刻把python 代码作为字符串并执行。第一个例子更加动态,但第二个突出了函数属性。

在运行时生成和执行Python 代码

第一个例子是loopmake.py 脚本,一个简单的、迅速生成和执行循环的计算机辅助软件工程(CASE)。它提示用户给出各种参数(比如,循环类型(while 或for), 迭代的数据类型[数字或序列]),生成代码字串,并执行它动态生成和执行Python 代码

dashes = '\n' + '-' * 50
print (dashes)exec_dict = {'f':"""for %s in %s:print %s""",'s':"""%s=0%s=%swhile %s< len(%s):print %s[%s]%s=%s+1""",'n':"""%s=%dwhile %s < %d:print %s%s = %s+%d"""}def main():ltype = input("Loop type? (For/While) ")dtype = input('Data type? (Number/Seq) ')if dtype == 'n':start = int(input('Starting value? '))stop = int(input('Ending value (non-inclusive)? '))step = int(input('Stepping value? '))seq = str(range(start, stop, step))else:seq = input('Enter sequence: ')var = input('Iterative variable name? ')if ltype == 'f':exec_str = exec_dict['f'] % (var, seq, var)elif ltype == 'w':if dtype == 's':svar = input('Enter sequence name? ')exec_str = exec_dict['s'] % (var, svar, seq, var, svar, svar, var, var, var)elif dtype == 'n':exec_str = exec_dict['n'] % (var, start, var, stop, var, var, var, step)print (exec_str)print (dashes)print ('Your custom-generated code:' + dashes)print (exec_str + dashes)print ('Test execution of the code:' + dashes)exec(exec_str)print (dashes)if __name__ == '__main__':main()

运行

Loop type? (For/While) f
Data type? (Number/Sequence) n
Starting value? 0
Ending value (non-inclusive)? 4
Stepping value? 1
Iterative variable name? counter
--------------------------------------------------
The custom-generated code for you is:
--------------------------------------------------
for counter in [0, 1, 2, 3]:
print counter
--------------------------------------------------
Test execution of the code:
--------------------------------------------------
0
1
2
3
--------------------------------------------------
% loopmake.py
Loop type? (For/While) w
Data type? (Number/Sequence) n
Starting value? 0
Ending value (non-inclusive)? 4
Stepping value? 1
Iterative variable name? counter
--------------------------------------------------
Your custom-generated code:
--------------------------------------------------
counter = 0
while counter < 4:
print counter
counter = counter + 1
--------------------------------------------------
Test execution of the code:
--------------------------------------------------
0
1
2
3
--------------------------------------------------
% loopmake.py
Loop type? (For/While) f
Data type? (Number/Sequence) s
Enter sequence: [932, 'grail', 3.0, 'arrrghhh']
Iterative variable name? eachItem
--------------------------------------------------
Your custom-generated code:
--------------------------------------------------
for eachItem in [932, 'grail', 3.0, 'arrrghhh']:
print eachItem
--------------------------------------------------
Test execution of the code:
--------------------------------------------------
932
grail
3.0
arrrghhh
--------------------------------------------------
% loopmake.py
Loop type? (For/While) w
Data type? (Number/Sequence) s
Enter sequence: [932, 'grail', 3.0, 'arrrghhh']
Iterative variable name? eachIndex
Enter sequence name? myList
--------------------------------------------------
Your custom-generated code:
--------------------------------------------------
eachIndex = 0
myList = [932, 'grail', 3.0, 'arrrghhh']
while eachIndex < len(myList):
print myList[eachIndex]
eachIndex = eachIndex + 1
--------------------------------------------------
Test execution of the code:
--------------------------------------------------
932
grail
3.0
arrrghhh
--------------------------------------------------

不同于raw_input(),input()会把输入当成python 表达式来求值,即使用户以字符串的形式输入,也会返回一个python 对象

为了很好地控制脚本的大小,从原来的脚本中剔除了所有的注释和错误检测。在本书的web站点上,都可以找到原来的和修改后的版本。

扩展的版本包括了额外的特性,比如用于字符串输入的不必要的引号,输入数据的默认值,以及检测无效的返回和标识符;也不允许以关键字和内建名字作为变量名字,有条件地执行代码。

第二个例子着重描写了在第11 章"函数"引入的函数属性,它是从Python 增强提议232(PEP 232)中的例子得到的灵感。假设你是一位负责质量控制的软件开发者,你鼓励你的工程师将回归测试或回归指令代码放到主代码中,但又不想让测试代码混合到产品代码中。你可以让工程师创建字符串形式的测试代码。当你的测试框架执行的时候,它会检测函数是否定义了测试体,如果是的话,(求值并)执行它。如果不是,便跳过,像通常一样执行。

Example 14.2  (funcAttrs.py):调用sys.exit()使python 解释器退出。exit()的任何整数参数作为退出状态会返回给调用者,该值默认为0

def foo():return Truedef bar():'bar() does not do much'return Truefoo.__doc__ = 'foo() does not do much'
foo.tester = '''
if foo():print ('PASSED')
else:print ('FAILED')
'''for eachAttr in dir():obj = eval(eachAttr)if isinstance(obj, type(foo)):if hasattr(obj, '__doc__'):print ('\nFunction "%s" has a doc string:\n\t%s' % (eachAttr, obj.__doc__))if hasattr(obj, 'tester'):print ('Function "%s" has a tester... executing'% eachAttr)exec(obj.tester)else:print ('"%s" is not a function' % eachAttr)

好了,真正的工作在这里开始。我们从用内建函数dir()迭代现在(即全局)名字空间开始。它返回的列表包含了所有对象的名字。因为这些都是字符串,我们需要在使用eval将它们转化为真正的python 对象。

除了预期的系统变量,比如,__builtins__,我们还期望显示函数。我们只对函数有兴趣。执行代码后,我们得到如下的输出:

"__builtins__" is not a function
"__doc__" is not a function
"__file__" is not a function
"__loader__" is not a function
"__name__" is not a function
"__package__" is not a function
"__spec__" is not a functionFunction "bar" has a doc string:bar() does not do muchFunction "foo" has a doc string:foo() does not do much
Function "foo" has a tester... executing
PASSED

14.4 执行其他(Python)程序

当讨论执行其他程序时,把它们分类为python 程序和非python 程序,后者包括了二进制可执行文件或其他脚本语言的源代码。先讨论如何运行其他的python 程序,然后是如何用os 模块调用外部程序。

14.4.1 导入

运行时刻有很多执行另外python 脚本的方法。正如我们先前讨论的,第一次导入模块会执行模块最高级的代码。不管你是否需要,这就是python 导入的行为。提醒,只有属于模块最高级的代码才是全局变量,全局类,和全局函数声明。

核心笔记:当模块导入后,就执行所有的模块,它运行所有最高级别的python 代码,比如,'main()’。如果foo 含有bar 函数的声明,那么便执行def foo(...)。再问一次为什么会这样做呢?由于某些原因,bar 必须被识别为foo模块中一个有效的名字,也就是说bar 在foo 的名字空间中,其次,解释器要知道它是一个已声明的函数,就像本地模块中的任何一个函数。现在我们知道要做什么了,那么如何处理那些不想每次导入都执行的代码呢?缩进它,并放入if __name__ == '__main__' 的内部。跟着应该是一个if 语句,它通过检测__name__来确定是否要调用脚本,比如,“if__name__ =='__main__'”。如果相等的话,你的脚本会执行main 内代码;否则只是打算导入这个脚本,那么可以在这个模块内对代码进行测试。

# import1.py
print 'loaded import1'
import import2======
# import2.py
print 'loaded import2'

这是当我们导入import1 时的输出

>>> import import1
loaded import1 loaded import2
>>>

根据建议检测__name__值的迂回工作法,我们改变了import1.py 和import2.py 里的代码,这样的情况就不会发生了,这里是修改后的import.py 版本:

# import1.py
import import2
if __name__ == '__main__':print 'loaded import1'

接着是import2.py 的代码,以相同的方式修改:

# import2.py
if __name__ == '__main__'print 'loaded import2'

当从python 中导入import1 的时候,我们不再会得到任何输出

>>> import import1

在某些情况中,你可能想要显示输出来确定输入模块。这取决于你自身的情况。我们的目标是提供实效的编程例子来屏蔽副作用。

14.4.2 execfile()

显然,导入模块不是从另外的python 脚本中执行python 脚本最可取的方法。那也就不是导入过程。导入模块的副作用是导致最高级代码运行。这章一开始,我们描述了如何通过文件对象,使用exec 语句来读取python 脚本的内容并执行。

下面的代码给出了例子:

f = open(filename, 'r')
exec f
f.close()

这3 行可以调用execfile()来换掉:

execfile(filename)

虽然上述代码执行了一个模块,但是仅可以在现有的执行环境下运行(比如,它自己的全局和局部的名字空间)。在某些情况下,可能需要用不同全局和局部的名字空间集合,而不是默认的集合来执行模块。execfile() 函数的语法非常类似于eval()函数的。

execfile(filename, globals=globals(), locals=locals())

类似eval(),globals 和locals 都是可选的,若不提供参数值的话,默认为执行环境的名字空间。如果只给定globals,那么locals 默认和globals 相同。如果提供locals 值的话,它可以是任何映射对象[一个定义/覆盖了__getitem__()的对象]。在2.4之前,locals 必须是一个字典。注意:(在修改的时候)小心局部名字空间。比较安全的做法是传入一个虚假的"locals"字典并检查是否有副作用。execfile()不保证不会修改局部名字空间。见python 库参kao手册对execfile()的解释

14.4.3 将模块作为脚本执行

python2.4 里加入了一个新的命令行选项(或开关),允许从shell 或DOS 提示符,直接把模块作为脚本来执行。当以脚本的方式来书写你的模块的时候,执行它们是很容易的。你可以使用命令行从你的工作目录调用你的脚本。

$ myScript.py # or $ python myScript.py

若模块是标准库的一部分,安装在site-packages 里,或者仅仅是包里面的模块,处理这样的模块就不是那么容易了,尤其是它们共享了已存在的同名python 模块。eg,要运行免费的python web服务器,以便创建和测试你自己的web页面和CGI 脚本。须在命令行敲入:

$ python /usr/local/lib/python2x/CGIHTTPServer.py
Serving HTTP on 0.0.0.0 port 8000 ...

这是段很长的命令,如果它是第三方的,不得不深入到site-packages 去找到它真正定位的地方。如果没给出完全的路径名,可以从命令行运行一个模块,并让python 的导入机制为我们做这种跑腿工作吗?是肯定的。可以用python -c 命令行开关:

$ python -c "import CGIHTTPServer; CGIHTTPServer.test()"

该选项允许指定你想要运行的python 语句。虽然它可以这样工作,但问题是__name__模块不是‘__main__‘而是你正在使用的模块。解释器通过import 装载了你的模块,并不是它当作脚本。所以,所有在if __name__ == '__main__' 之下的代码是不会执行的,所以你不得不手动地调用模块的test()函数,就如同前面我们所做的一样。所以我们想同时要两者的优点——能够在类库中执行作为脚本的模块而不是作为导入的模块。这就是-m 参数的动机。现在可以像这样运行脚本:

$ python -m CGIHTTPServer

这是不小的改进。尽管如此,还没有完全如预想那样实现特性。所以在python2.5 中,-m 开关有了更多的兼容性。从2.5 开始,你可以用相同的参数来运行包内或需要特别加载的模块,比如zip文件里的模块,这是在2.3 加入的特性(12.5.7 小节,396 页)。python2.4 只让你执行标准的库模块。所以初始版本的-m 选项是不能运行特殊的模块如PyCHecker(python 的lint),或其他的profiler(注意这些是装载和运行其他模块的模块)。但是2.5 版本解决了这个问题。

14.5 执行其他(非Python)程序

在python 程序里也可以执行非python 程序。这些程序包括了二进制可执行文件,其他的shell 脚本等等。所有的要求只是一个有效的执行环境,比如,允许文件访问和执行,脚本文件必须能访问它们的解释器(perl, bash,等等),二进制必须是可访问的(和本地机器的构架兼容)最终,程序员必须kao虑python 脚本是否必须和其他将要执行的程序通信。有些程序需要输入,而有的程序返回输出以及执行完成时的错误代码,也许有的两者都做。针对不同的环境,python 提供了各种执行非python 程序的方法。本节讨论的所有函数都可以在os 模块中找到。在表14.6 中,我们做了总结(我们会对那些只适合特定平台的函数进行标注),作为对本节剩余部分的介绍。

随着越来越接近软件的操作系统层面,你就会发现执行跨平台程序(甚至是python 脚本)的一致性开始有些不确定了。在这个小节中描述的程序在os 模块中。事实上,有多个os模块。比如说,基于Unix 衍生系统(例如Linux,MacOS X, Solaris,BSD 等等)的模块是posix 模块,windows 的是nt(无论你现在用的是哪个版本的windows;dos 用户有dos 模块),旧的macOS 为mac 模块。不用担心,当你调用import os 的时候,python 会装载正确的模块。你不需要直接导入特定的操作系统模块。

在我们看看每个模块函数之前,对于python2.4 或者更新版本的用户,这里有个subprocess 模块,可以作为上面所有函数很好的替代品。本章稍后演示如何使用这些函数,然后在最后给出subprocess.Popen 类和subprocess.call()函数的等价使用方法。

14.5.1 os.system()

第一个函数是system(),接收字符串形式的系统命令并执行它。当执行命令的时候,python 的运行是挂起的。当执行完成之后,将会以system()的返回值形式给出退出状态,python 的执行也会继续。

system()保留了现有的标准文件,包括标准的输出,意味着执行任何的命令和程序显示输出都会传到标准输出上。这里要当心,因为特定应用程序比如公共网关接口(CGI),如果将除了有效的超文本标示语言(HTML)字符串之外的输出,经过标准输出发送回客户端,会引起web 浏览器错误。system()通常和不会产生输出的命令一起使用,其中的一些命令包括了压缩或转换文件的程序,挂载磁盘到系统的程序,或其他执行特定任务的命令---通过退出状态显示成功或失败而不是通过输入和/或输出通信。利用退出状态,0 表示成功,非零表示其他类型的错误。

为了给出一个例子,我们执行了两个从交互解释器中获取程序输入的命令,这样你便可以观察system()是如何工作的:

>>> import os
>>> result = os.system('cat /etc/motd') Have a lot of fun...
>>> result
0
>>> result = os.system('uname -a')
Linux solo 2.2.13 #1 Mon Nov 8 15:08:22 CET 1999 i586 unknown
>>> result
0

可以看到两个命令的输出和它们执行的退出状态。下面是一个执行dos 命令的例子:

14.5.2 os.popen()

popen()函数是文件对象和system()函数的结合,工作方式和system()相同,但它可以建立一个指向那个程序的单向连接,然后如访问文件一样访问这个程序。如果程序要求输入,要用'w'模式写入那个命令来调用popen()。发送给程序的数据会通过标准输入接收到。

同样地,'r'模式允许spawn 命令,当它写入标准输出的时候,你就可以通过类文件句柄使用熟悉的file 对象的read*()方法来读取输入。就像对于文件,当使用完毕以后,你应当close()连接。调用unix 程序uname 提供机器和使用的操作系统的相关信息。该命令产生了一行输出,并直接写到屏幕上。如果想要把该字符串读入变量中并执行内部操作或者把它存储到日志文件中,我们可以使用popen()。实际上,代码如下所示:

>>> import os
>>> f = os.popen('uname -a')
>>> data = f.readline()
>>> f.close()
>>> print data,
Linux solo 2.2.13 #1 Mon Nov 8 15:08:22 CET 1999 i586 unknown

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

14.5.3 os.fork(), os.exec*(),os.wait*()

我们不会对操作系统理论做详尽的介绍,只是稍稍地介绍一下进程(process)。fork()采用称为进程的单一执行流程控制,可称之为创建“岔路口”。用户系统同时接管了两个fork——也就是说让用户拥有了两个连续且并行的程序。(不用说,它们运行的是同一个程序,因为两个进程都是紧跟在fork()调用后的下一行代码开始执行的)。调用fork()的原始进程称为父进程,而作为该调用结果新创建的进程则称为子进程。当子进程返回的时候,其返回值永远是0;当父进程返回时,其返回值永远是子进程的进程标识符(又称进程ID,或PID)(这样父进程就可以监控所有的子进程了)PID 也是唯一可以区分他们的方式!我们提到了两个进程会在调用fork()后立刻运行。因为代码是相同的,如果没有其他的动作,我们将会看到同样的执行结果。而这通常不是我们想要的结果。创建另外一个进程的主要目的是为了运行其他程序,所以必须在父进程和子进程返回时采取分流措施。正如上面我们所说,它们的PID 是不同的,而这正是我们区分它们的方法。

对于那些有进程管理经验的人来说,接下来的这段代码是再熟悉不过了:

ret = os.fork() # spawn 2 processes, both return #产生两个进程,都返回
if ret == 0: # child returns with PID of 0 #子进程返回的PID 是0child_suite # child code #子进程的代码
else: # parent returns with child's PID #父进程返回是子进程的PIDparent_suite # parent code #父进程的代码

在代码第一行便调用了fork()。现在子进程和父进程同时在运行。子进程本身有虚拟内存地址空间的拷贝,以及一份父进程地址空间的原样拷贝。-----是的,两者几乎都是相同的。fork()返回两次,意味着父进程和子进程都返回了。如何区分两者呢?当父亲返回的时候,会带有进程的PID。而当子进程返回的时候,其返回值为0。

利用if-else 语句,给子进程和父进程指定各自的执行代码。在子进程的代码中,可以调用任何exec*()函数来运行完全不同的程序,或者同一个程序中的其他的函数(只要子进程和父进程用不同的路径执行)。普遍做法是让子进程做所有的脏活,而父进程耐心等来子进程完成任务,或继续执行,稍后再来检查子进程是否正常结束。

所有的exec*()函数装载文件或者命令,并用参数列表(分别给出或作为参数列表的一部分)来执行它。如果适用的话,也可以给命令提供环境变量字典。这些变量普遍用于给程序提供对当前执行环境的精确描述。其中一些著名的变量包括用户的名字,搜索路径,现在的shell,终端类型,本地化语言,机器类型,操作系统名字等等。

所有版本的exec*()都会用给定文件作为现在要执行的程序取代当前(子)进程的Python 解释器。和system()不一样,对于Python 来说没有返回值(因为Python 已经被替代了)。如果因为某种原因,程序不能执行,那么exec*()就会失败,进而导致引发异常。

接下来的代码在子进程中开始了一个称为“xbill"的可爱小巧的游戏,而父进程继续运行Python解释器。因为子进程从不返回,所以无需去顾虑调用exec*()后的子进程代码。注意该命令也是参数列表中的必须的第一个参数。

ret = os.fork()
if ret == 0: # child code #子进程代码execvp('xbill', ['xbill'])
else: # parent code #父进程代码 os.wait()

可以看到对wait()的调用。当子进程执行完毕,需要它们的父进程进行扫尾工作。这个任务,称为”收获孩子”(reaping a child),可以用wati*()函数完成。紧跟在fork()之后,父进程可以等待子进程完成并在那进行扫尾。父进程也可以继续运行,稍后再扫尾,同样也是用wait*()函数中的一个。不管父进程选择了那个方法,该工作都必须进行。当子进程完成执行,还没有被收获的时候,它进入了闲置状态,变成了著名的僵尸进程。在系统中,应该尽量把僵尸进程的数目降到最少,因为在这种状态下的子进程仍保留着在存活时期分配给它们的系统资源,而这些资源只能在父进程收获它们之后才能释放掉。

调用wait()会挂起执行(比如,waits),直到子进程(其他的子进程)正常执行完毕或通过信号终止。wait()将会收获子进程,释放所有的资源。如果子进程已经完成,那么wait()只是进行些收获的过程。waitpid()具有和wait()相同的的功能,但是多了一个参数PID(指定要等待子进程的进程标识符),以及选项(通常是零或用‘OR’组成的可选标志集合)

14.5.4 os.spawn*()

函数spawn*()家族和fork,exec*()相似,因为它们在新进程中执行命令;然而,你不需要分别调用两个函数来创建进程,并让这个进程执行命令。你只需调用一次spawn*()家族。由于其简单性,你放弃了“跟踪”父进程和子进程执行的能力;该模型类似于在线程中启动函数。还有点不同的是你必须知道传入spawn*()的魔法模式参数。在其他的操作系统中(尤其是嵌入式实时操作系统[RTOS]),spawn*()比fork()快很多。不是这种情况的操作系统通常使用写实拷贝(copy-on-write)技术。参阅python 库参kao手册来获得更多spanw*()的资料。各种spanw*()家族成员是在1.5 和1.6(含1.6)之间加入的。

14.5.5 subprocess 模块

在python2.3 出来之后,一些关于popen5 模块的工作开始展开。一开始该命名继承了先前popen*()函数的传统,但是并没有延续下来,该模块最终被命名为subproess,其中一个类叫Popen,集中了我们在这章讨论的大部分面向进程的函数。同样也有名为call()的便捷函数,可以轻易地取代了os.system()。在python2.4 中,subprocess 初次登场。下面就是演示该模块的例子:替换 os.system()Linux 上的例子:

>>> from subprocess import call
>>> import os
>>> res = call(('cat', '/etc/motd'))
Linux starship 2.4.18-1-686 #4 Sat Nov 29 10:18:26 EST 2003 i686
GNU/Linux
>>> res
0

Win32 例子取代os.popen(),创建Popen()实例的语法只比调用os.popen()函数复杂了一点

>>> from subprocess import Popen, PIPE
>>> f = Popen(('uname', '-a'), stdout=PIPE).stdout
>>> data = f.readline()
>>> f.close()
>>> print data,
Linux starship 2.4.18-1-686 #4 Sat Nov 29 10:18:26 EST 2003 i686
GNU/Linux
>>> f = Popen('who', stdout=PIPE).stdout
>>> data = [ eachLine.strip() for eachLine in f ]
>>> f.close()
>>> for eachLine in data:
... print eachLine
...

14.5.6 相关函数

表14.7 列出了可以执行上述任务的函数(及其模块)

14.6 受限执行

在python 历史某个时期内,存在着使用了rexec 和bastion 模块的限制执行的概念。第一个模块允许沙盒(sandbox)中的执行代码修改内建对象。第二个模块用来过滤属性和包装你的类。然而,由于一个显著的缺点和弥补安全漏洞的困难,这些模块便被废弃了。那些维护使用了这些模块的老代码的人员可能会用到这两个模块的文档。

14.6 结束执行

当程序运行完成,所有模块最高级的语句执行完毕后退出,我们便称这是干净的执行。可能有很多情况,需要从python 提前退出,比如某种致命错误,或是不满足继续执行的条件的时候。在python 中,有各种应对错误的方法。其中之一便是通过异常和异常处理。

另外一个方法便是建造一个“清扫器”方法,这样便可以把代码的主要部分放在if 语句里,在没有错误的情况下执行,因而可以让错误的情况“正常地“终结。然而,有时也需要在退出调用程序的时候,返回错误代码以表明发生何种事件。

14.7.1 sys.exit() and SystemExit

立即退出程序并返回调用程序的主要方式是sys 模块中的exit()函数。sys.exit()的语法为:

sys.exit(status=0)

当调用sys.exit()时,就会引发systemExit()异常。除非对异常进行监控(在一个try 语句和合适的except 子句中),异常通常是不会被捕捉到或处理的,解释器会用给定的状态参数退出,如果没有给出的话,该参数默认为0。System Exit 是唯一不看作错误的异常。它仅仅表示要退出python的愿望。

sys.exit()经常用在命令调用的中途发现错误之后,比如,如果参数不正确,无效,或者参数数目不正确。下面的例子14.4(args.py)仅仅是一个测试脚本,在正确执行之前需要给出确定数目的参数。

执行这个脚本我们得到如下输出:

$ args.py
At least 2 arguments required (incl. cmd name). usage: args.py arg1 arg2
[arg3... ]
$ args.py XXX
At least 2 arguments required (incl. cmd name). usage: args.py arg1 arg2 [arg3... ]
$ args.py 123 abc
number of args entered: 3
args (incl. cmd name) were: ['args.py', '123', 'abc']
$ args.py -x -2 foo
number of args entered: 4
args (incl. cmd name) were: ['args.py', '-x', '-2',
'foo']
Example 14.4 Exiting Immediately (args.py) 立即退出 (args.py)

调用sys.exit()使python 解释器退出。exit()的任何整数参数都会以退出状态返回给调用者,该值默认为0;

1 #!/usr/bin/env python

2

3 import sys

4

5 def usage():

6 print 'At least 2 arguments (incl. cmd name).'

7 print 'usage: args.py arg1 arg2 [arg3... ]'

8 sys.exit(1)

9

10 argc = len(sys.argv)

11 if argc < 3:

12 usage()

13 print "number of args entered:", argc

14 print "args (incl. cmd name) were:", sys.argv

许多命令行驱动的程序在进行之前,用脚本的核心功能测试了输入的有效性。如果验证失败,那么便调用usage()函数去告知用户什么样的问题会导致这个错误,并"提示"用户如何才能正确地调用脚本。

14.7.2 sys.exitfunc()

sys.exitfunc()默认是不可用的,但你可以改写它以提供额外的功能。当调用了sys.exit()并在解释器退出之前,就会用到这个函数了。这个函数不带任何参数的,所以你创建的函数也应该是无参的。

如果sys.exitfunc 已经被先前定义的exit 函数覆盖了,最好的方法是把这段代码作为你exit()函数的一部分来执行。一般说来,exit 函数用于执行某些类型的关闭活动,比如关闭文件和网络连接,最好用于完成维护任务,比如释放先前保留的系统资源。

下面的例子介绍了如何设置exit()函数,如果已经被设置了,则确保执行该函数:

import sys

prev_exit_func = getattr(sys, 'exitfunc', None)

def my_exit_func(old_exit = prev_exit_func):

# :

# perform cleanup 进行清理

# :

if old_exit is not None and callable(old_exit):

old_exit()

sys.exitfunc = my_exit_func

在清理执行以后,我们执行了老的exit()函数。getattr()调用只是检查了先前的exitfunc()是否已经定义。如果没有,那么prev_exit_func 赋值为None,否则, prev_exit_func 变成exit 函数新的别名,然后作为参数传入我们的新exit 函数,my_exit_func。

对getattr()的调用可以这样写:

if hasattr(sys, 'exitfunc'):

prev_exit_func = sys.exitfunc # getattr(sys, 'exitfunc')

else:

prev_exit_func = None

14.7.3 os._exit() Function os._exit() 函数

os 模块的_exit()函数不应该在一般应用中使用。(平台相关,只适用特定的平台,比如基于Unix的平台,以及Win32 平台)。其语法为:

os._exit(status)

这个函数提供的功能与sys.exit()和sys.exitfunc()相反,根本不执行任何清理便立即退出python。与sys.exit()不同,状态参数是必需的。通过sys.exit()退出是退出解释器的首选方法。

14.7.4 os.kill() Function

os 模块的kill()函数模拟传统的unix 函数来发送信号给进程。kill()参数是进程标识数(PID)和你想要发送到进程的信号。发送的典型信号为SIGINT, SIGQUIT,或更彻底地,SIGKILL,来使进程终结。

14.8 各种操作系统接口

在一章中,我们已看到各种通过os 模块和操作系统进行交互的方法。我们看到的大多数函数都是处理文件或外部进程执行。这里有些 方法允许对现在的用户和进程有较特殊的动作,我们将简要地看看。表14.8 中描述的大部分函数只在posix 系统上工作,除非标明了适用于Windows 环境。

14.9 相关模块

在表14.9 中, 除了os 和sys 模块,你还可以找到与这章执行环境主题相关的模块列表。

转载于:https://my.oschina.net/cqlcql/blog/662512

python浓缩(14)执行环境相关推荐

  1. JavaScript执行环境及作用域(一)——执行环境栈和作用域链机制

    2019独角兽企业重金招聘Python工程师标准>>> 执行环境是JavaScript中最为重要的一个概念,每个执行环境都有一个与之关联的变量对象,执行环境中所有的变量和函数都保存在 ...

  2. [PYTHON] 核心编程笔记(14.Python执行环境)

    在python中有多种运行外部程序的方法,比如,运行操作系统命令或另外的python脚本,或执行一个磁盘上的文件,或通过网络来运行文件 在特定执行场景包括: 在当前脚本继续运行 创建和管理子进程 执行 ...

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

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

  4. python文件可以执行的环境有哪些呢

    Python文件可以在多种环境中执行,其中一些常见的环境包括: 命令行环境:通过命令行终端运行 Python 文件,使用 python 命令来执行. 集成开发环境(IDE):例如 PyCharm.VS ...

  5. 软件测试学习 之 Python 两种执行方式——python和python -m 的区别

    笔者使用的操作系统为windows 7,python解释器版为python3.7,pycharm版本为pycharm2018.3 如无特殊说明,代码均在此环境执行 目录 一般结论 简单例子 模块 作为 ...

  6. Python+Eclipse+PyDev开发环境搭建及Prophet工具的使用

    我使用的是python3.6+eclipse4.7+PyDev5.7,这三个版本之间是兼容的,如果版本不兼容,搭建环境时会出现很多问题.以下是几个建议和注意事项: (1)   python版本在3.5 ...

  7. 1.python学习工具与环境安装

    python学习工具与环境安装 因为上传的时候图片出现了些问题,详细的图片可以下载博客内PNG或者PDF版图片版进行观看 无python环境 有python环境 未使用过anaconda管理pytho ...

  8. 我的全栈之路-Python基础之Python概述与开发环境搭建

    我的全栈之路-Python基础之Python概述与开发环境搭建 我的全栈之路 1.1 信息技术发展趋势 1.2 浅谈计算机系统架构 1.2.1 计算机系统架构概述 1.2.2 计算机硬件系统 1.2. ...

  9. python 二进制流转图片_Python零基础入门到精通-5.1节:Python程序的执行过程

    教程引言: 系统地讲解计算机基础知识,Python的基础知识, 高级知识,web开发框架,爬虫开发,数据结构与算法,nginx, 系统架构.一步步地帮助你从入门到就业. 5.1.1 在命令行中执行Py ...

最新文章

  1. 游戏引擎算法探究:游戏核心之固定流水线
  2. VS2005 .vs. Orcas
  3. 1073. Pearls
  4. Ubuntu16.04 sudo apt-get install lib***-dev安装失败,无法锁定文件,sudo apt-get update 更新失败也无法解决
  5. 强强合体:Docker版Kali Linux发布
  6. 【英语学习】【WOTD】liaison 释义/词源/示例
  7. “秃如其来”的90后,造就了一个蒸蒸日上的产业
  8. shell脚本文件中ll提示找不到命令
  9. 数学基础加强1---机器学习与数学分析
  10. python从数据库中取出文件保存到excel,csv表格中的办法:
  11. Adobe Premiere(Pr视频剪辑)下载安装
  12. VMware虚拟机安装操作系统方法
  13. 常见的测试用例设计方法8---正交试验法
  14. 推荐一款前端轻量级的toolTip插件-Tippy.js
  15. 基于遗传算法的TSP和MTSP问题求解(python)
  16. Telink Mesh简单安卓app分析
  17. MaxCompute SQL大数据公开数据集实战
  18. 海信合作徕卡首款激光电视将于9月亮相;SK海力士成功研发全球最高层238层4D NAND闪存 | 美通企业日报...
  19. php微信企业号oauth2.0,微信企业号:OAuth2.0微信授权验证
  20. SQOOP 部署及导入数据到 Hive 的实际应用

热门文章

  1. 2016年华为网络技术精英大赛复赛试题
  2. 软件设计模式—面向接口编程
  3. 【2017-02-20】C#基础 - 运算符//表格,示例,代码巩固练习
  4. 第一天接触网络电话,熟悉了sip协议
  5. “毕设导师互选系统”项目产品宣传推广方案
  6. FactoryMethodPattern(工厂方法)
  7. 《RESTful Web Services》第一章 使用统一接口
  8. JS日期函数getMonth()的值域是0--11
  9. 耿建超英语语法---被动语态
  10. 高级T-SQL第1级的阶梯:使用交叉连接来引入高级T-SQL