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

简介:

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




1.可调用对象


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


(1)函数

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

  • 内建函数(BIF)

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

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

1
2
3
4
5
>>>  type ( dir )
< type  'builtin_function_or_method' >
>>> 
>>>  type ( 'xpleaf' .upper)
< type  'builtin_function_or_method' >

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

1
2
>>>  type ( int )
< type  'type' >

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

  • 用户定义的函数(UDF)

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

1
2
3
4
>>>  def  foo(): pass
... 
>>>  type (foo)
< type  'function' >

    UDF本身也有很多属性:

1
2
>>>  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>":

1
2
3
4
5
6
7
8
9
10
11
>>> lambdaFunc  =  lambda  x: x * 2
>>> lambdaFunc( 100 )
200
>>>  type (lambdaFunc)
< type  'function' >
>>> 
>>> lambdaFunc.__name__
'<lambda>'
>>> 
>>> foo.__name__
'foo'


(2)方法

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

  • 内建方法(BIM)

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

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

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

1
2
3
4
5
6
7
8
9
>>>  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都是相同的类型,即“实例方法”,无论该方法是否有绑定的实例:

1
2
3
4
5
6
7
8
9
10
>>>  class  C( object ):
...      def  foo( self ):
...              pass
... 
>>> c  =  C()
>>> 
>>>  type (C.foo)
< type  'instancemethod' >
>>>  type (c.foo)
< type  'instancemethod' >

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

1
2
3
4
>>> 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__()方法,参数的设置跟类的构造器原理是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>>  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属性,其实就是指代码对象:

1
2
3
4
>>>  def  foo():  pass
... 
>>> foo.func_code
<code  object  foo at  0x7f37a5daceb0 file  "<stdin>" , line  1 >




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


    常见的相关函数如下:

可执行的对象声明和内建函数
内建函数和语句 描述
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=''))


(1)callable()

    callable()是一个布尔函数,确定一个对象是否可以通过函数操作符(())来调用:

1
2
3
4
5
6
7
8
>>>  callable ( dir )
True
>>>  callable ( 1 )
False
>>>  def  foo():  pass
... 
>>>  callable (foo)
True


(2)compile()

    compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用exec语句或者内建函数eval()来执行这些代码对象或者对它们进行求值。一个很重要的观点是:exec和eval()都可以执行字符串格式的Python代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。compile()函数提供了一次性字节代码预编译,以后每次调用的时候,就不用编译了。

    compile的三个参数都是必需的,第一参数借助了要编译的Python代码。第二个字符串,虽然是必需的,但通常被设置为空串。该参数代表了存放代码对象的文件的名字(字符串类型)。compile的通常用法是动态生成字符串形式的Python代码,然后生成一个代码对象——代码显然没有存放在任何文件,所以文件名就通常设置为空了。

    最后的参数是个字符串,它用来表明代码对象的类型,有三个可能值:

'eval' 可求值的表达式,和eval()一起使用
'single' 单一可执行语句,和exec一起使用
'exec' 可执行语句组,和exec一起使用
  • 可求值表达式

1
2
3
4
5
>>> eval_code  =  compile ( '100+200' , ' ', ' eval ')
>>> eval_code
<code  object  <module> at  0x7f37a5db3cb0 file  "", line  1 >
>>>  eval (eval_code)
300
  • 单一可执行语句

1
2
3
4
5
>>> single_code  =  compile ( 'print "Hello world!"' , ' ', ' single')
>>> single_code
<code  object  <module> at  0x7f37a3c2a0b0 file  "", line  1 >
>>>  exec  single_code
Hello world!
  • 可执行语句组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 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


(3)eval()

    eval()对表达式求值,第一个参数可以为字符串或compile()创建的预编译代码对象。第二个和第三个参数为可选的,分别默认为globals()和locals()返回的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> a  =  3
>>>  eval ( 'a' )
3
>>>  def  test_eval():
...   a  =  6
...    print  eval ( 'a' )
... 
>>> test_eval()
6
>>
>>> scope = {}
>>> scope[ 'a' =  3
>>> scope[ 'b' =  4
>>> result  =  eval ( 'a+b' ,scope)
>>> result
7


(4)exec

    exec语句只接受一个参数,即exec obj,obj除了可以是原始的字符串(单一语句或语句组)或是compile()编译的代码对象外,也可以是文件对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> f  =  open ( 'xcount.py' )
>>>  for  eachLine  in  f:
...    print  eachLine,
... 
=  0
print  'x is currently:' , x
while  x <  5 :
     + =  1
     print  'incrementing x to:' , x
 
>>> 
>>> f.tell()
86
>>> f.seek( 0 )
>>> 
>>>  exec  f
is  currently:  0
incrementing x to:  1
incrementing x to:  2
incrementing x to:  3
incrementing x to:  4
incrementing x to:  5
>>> 
>>> f.tell()     # 执行完之后,就指针就停留在文件末尾(end-of-line,EOF)
86


(5)input()

    内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input()),这说明input()和raw_input()本身还是有区别的。

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

    举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> aString  =  raw_input ( 'Enter a list: ' )
Enter a  list : [ 'xpleaf' 'clyyh' ]
>>> aString
"['xpleaf', 'clyyh']"
>>>  type (aString)
< type  'str' >
>>> 
>>> aList  =  input ( 'Enter a list: ' )
Enter a  list : [ 'xpleaf' 'clyyh' ]
>>> aList
[ 'xpleaf' 'clyyh' ]
>>>  type (aList)
< type  'list' >


(6)使用Python在运行时生成和执行Python代码

    有需要时可以参考书本上的例子。





本文转自 xpleaf 51CTO博客,原文链接:http://blog.51cto.com/xpleaf/1795129,如需转载请自行联系原作者

相关文章
|
2月前
|
PyTorch Linux 算法框架/工具
pytorch学习一:Anaconda下载、安装、配置环境变量。anaconda创建多版本python环境。安装 pytorch。
这篇文章是关于如何使用Anaconda进行Python环境管理,包括下载、安装、配置环境变量、创建多版本Python环境、安装PyTorch以及使用Jupyter Notebook的详细指南。
302 1
pytorch学习一:Anaconda下载、安装、配置环境变量。anaconda创建多版本python环境。安装 pytorch。
|
26天前
|
机器学习/深度学习 数据可视化 Docker
Python环境
Python环境
34 3
|
1月前
|
弹性计算 Linux iOS开发
Python 虚拟环境全解:轻松管理项目依赖
本文详细介绍了 Python 虚拟环境的概念、创建和使用方法,包括 `virtualenv` 和 `venv` 的使用,以及最佳实践和注意事项。通过虚拟环境,你可以轻松管理不同项目的依赖关系,避免版本冲突,提升开发效率。
79 3
|
2月前
|
存储 缓存 Java
深度解密 Python 虚拟机的执行环境:栈帧对象
深度解密 Python 虚拟机的执行环境:栈帧对象
70 13
|
2月前
|
IDE 网络安全 开发工具
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
本文介绍了如何在PyCharm专业版中连接远程服务器并配置远程Python环境解释器,以便在服务器上运行代码。
388 0
IDE之pycharm:专业版本连接远程服务器代码,并配置远程python环境解释器(亲测OK)。
|
2月前
|
机器学习/深度学习 缓存 PyTorch
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
这篇文章是关于如何下载、安装和配置Miniconda,以及如何使用Miniconda创建和管理Python环境的详细指南。
451 0
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
|
2月前
|
Python Windows
利用Python在Win10环境下实现拨号上网
利用Python在Win10环境下实现拨号上网
41 4
|
2月前
|
TensorFlow 算法框架/工具 虚拟化
python开发先创建虚拟环境呀
python开发先创建虚拟环境呀
23 1
|
3月前
|
项目管理 Python
如何在Mac上安装多个Python环境
在你的Mac上使用多个Python环境可以对项目管理很有帮助,特别是在同时处理不同Python版本或不同的包需求时。在这篇文章中,我们将向你展示如何在Mac上轻松地安装和管理多个Python环境。
94 5
 如何在Mac上安装多个Python环境
|
2月前
|
网络安全 开发者 Python
VSCode远程切换Python虚拟环境
VSCode远程切换Python虚拟环境
79 1