python和php的作用域及生命周期

来源:互联网 发布:阿里巴巴数据管家 编辑:程序博客网 时间:2024/03/29 02:24

python和php支持的数据类型

编程语言中的数据无非变量、常量(宏定义,用来定义常量 ,php使用define函数或是在类中使用const关键字)

常量一般只允许是字符串或者数字。变量可能是该语言所支持的任意一种数据类型,如:

PHP所支持的数据类型:

四种标量类型:

string(字符串)

integer(整型)

float(浮点型,也作 double )

boolean(布尔型)

两种复合类型:

array(数组)

object(对象)

两种特殊类型:

resource(资源)

NULL(空)

python所支持的数据类型

字符串、整型、浮点型、布尔类型

列表、元组、集合、字典 

None 空

在python中所以并没有把对象当做一种特殊的数据类型。


作用域


作用域的概念

使用python举例:通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域(代码文本区域)。说一个变量具有xx作用域,就是在这个范围内,可以直接使用。


命名空间则是一个代码实体,它实现了名称到对象的映射关系。可以暂且把命名空间和作用域看做是等价的概念。命名空间是实现某个作用域的基础。命名空间会在不同的时间创建,并有不同的生命周期。包含内置名称的命名空间是在python解释器启动的时候创建的,并且不会被删除。一个模块的全局命名空间是在模块定义代码被读入的时候创建的,一般情况下,模块命名空间会持续到解释器结束。



我们关注作用域(背后是不同的命名空间在起作用),以确定变量和常量定义或者赋值以后,可以被使用的范围


局部(本地作用域,可以拥有嵌套作用域)

文件全局(全局作用域)

进程全局的(内置作用域,拥有整个进程的作用域)


不讨论的范畴


引入类后,会引入新的封闭的作用域,已经在面向对象中单独讨论,python built-in模块中的函数和变量,php也有超全局变量,属于进程中生效的,可以看做这些变量具有内置作用域不再讨论


php常量一旦被声明,即define过,将可以在全局可见,拥有内置作用域。python则没有常量,也没有define这一说。


大致的共性

变量无非局部变量和全局变量。php和python都是根据变量定义的位置来确定它是局部变量和全局变量。python能够改变变量作用域的代码段是def、class、lamda。php只有前两者。有如下叙述:


1、在一个脚本中声明的全局变量在整个脚本中是可见的。在函数的本地作用域中,python可以直接引用全局变量,php需要使用global关键字声明,防止全局变量被无意间改变


2、在函数内部创建的变量对函数来说是本地的,而当函数终止时,该变量也就不存在了。 


关于变量搜索时作用域优先级关系,即同名不同域覆盖关系如图,这是一个在各个语言中相当普遍的规则(以python为例)。当在函数中使用未确定的变量名时,Python搜索4个作用域:本地作用域(L locals()函数返回的字典),之后是上一层嵌套结构中 def 或 lambda 的本地作用域(E),之后是全局作用域(G globals函数返回的字典),最后是内置作用域(B)。按这个查找原则,在第一处找到的地方停止。如果没有找到,Python 会报错的。 

函数嵌套和引用机制带来的作用域差别

虽然我们应该避免使用函数嵌套。单不得不说,在嵌套作用域上面,php和python还是有显著不同的。python因为LEGB搜索原则,上层嵌套的变量是可以直接在下层使用。而php则不可以直接使用。


def func1():

        a = 'var'

        def func2():

                print a

        func2()


func1()



通过资源引用,可以扩大一个文件中全局资源(全局变量、函数、类)的作用域到其他的文件。在引用机制和引用带来的作用改变方面php和python完全不同。


python文件在被import以后,只是将资源加入了文件的全局作用域或是本地作用域(根据import的位置不同)


php的require则是文件内容引入。引入以后,无论位置在哪里,类和函数都具有全局作用域,而变量则只有本地作用域。



python的import机制


一个被加载到内存中的代码文件,作为一个模块对象而存在,它的__dict__属性中以key-value的形式存放着这个模块拥有的资源。Python 中所有加载到内存的模块都放在 sys.modules 。当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将模块的名字加入到正在调用 import 的模块的 Local 名字空间中。如果没有加载则从 sys.path 目录中按照模块名称查找模块文件,模块可以是py、pyc、pyd,找到后将模块载入内存,并加到 sys.modules 中,并将名称导入到当前的 Local 名字空间(意思是如果在函数内部import,那么只在函数本地作用域可用被引入的资源 变量、函数、类)

def func1():

        if 1:

                from b import b

                b.func()


func1()

#b.func() 报错


一个模块不会重复载入。多个不同的模块都可以用 import 引入同一个模块到自己的 Local 名字空间,其实背后的 PyModuleObject 对象只有一个。


import的过程:

RobertChen:这跟Python内部 import 的机制是有关的,具体到 from B import D,Python 内部会分成几个步骤:

        (1)在 sys.modules 中查找符号 “B”

        (2)如果符号 B 存在,则获得符号 B 对应的 module 对象。

                从 <modult B> 的 __dict__ 中获得符号 “D” 对应的对象,如果 “D” 不存在,则抛出异常。

        (3)如果符号 B 不存在,则创建一个新的 module 对象 <module B>,注意,此时,module 对象的 __dict__ 为空。

                执行 B.py 中的表达式,填充 <module B> 的 __dict__。

                从  <module B> 的 __dict__ 中获得 “D” 对应的对象,如果 “D” 不存在,则抛出异常。


对于嵌套import,需要注意的一点就是各个模块的 Local 名字空间是独立的。所以,本模块导入 A 模块(import A),A 中又 import B,B 模块又可以 import 其他模块,本模块 import A 之后本模块只能访问模块 A,不能访问模块 B 及其他模块。虽然模块 B 已经加载到内存了,如果访问还要再明确的在本模块中 import B。


对于循环嵌套,会出错

文件[ A.py ]

                   from B import D

                   class C:pass


                   文件[ B.py ]

                   from A import C

                   class D:pass

        为什么执行 A 的时候不能加载 D 呢?

        如果将 A.py 改为:import B 就可以了。

        这是怎么回事呢?


1、执行 A.py 中的 from B import D 由于是执行的 python A.py,所以在 sys.modules 中并没有 <module B> 存在, 首先为 B.py 创建一个 module 对象 (<module B>) , 注意,这时创建的这个 module 对象是空的,里边啥也没有, 在 Python 内部创建了这个 module 对象之后,就会解析执行 B.py,其目的是填充 <module B> 这个 __dict__。 

          2、执行 B.py中的from A import C 在执行B.py的过程中,会碰到这一句, 首先检查sys.modules这个module缓存中是否已经存在<module A>了, 由于这时缓存还没有缓存<module A>, 所以类似的,Python内部会为A.py创建一个module对象(<module A>), 然后,同样地,执行A.py中的语句

          3、再次执行A.py中的from B import D 这时,由于在第1步时,创建的<module B>对象已经缓存在了sys.modules中, 所以直接就得到了<module B>, 但是,注意,从整个过程来看,我们知道,这时<module B>还是一个空的对象,里面啥也没有, 所以从这个module中获得符号"D"的操作就会抛出异常。 如果这里只是import B,由于”B"这个符号在sys.modules中已经存在,所以是不会抛出异常的。



可变对象dic1在执行b.py的开始就随文件缓存在了sys.modules里面。b中拿到对象引用后,修改后生效。c中再次拿到对象修改。如果是b是一个不可变对象,则没有这样的效果。因为这是传址。而不可变对象就是传值,每个模块拿到引用后,都各自模块中生效。


PHP的require机制


跟C的资源引用有点类似,是读入并执行文件,替代引用语句。是一个大文件的概念。与python完全不同。


require的使用方法如:require(“myfile.php”),这个语句通常放在PHP脚本程序的最前面。PHP程序在执行前的伪编译过程中,就会先读入require()语句所引入的文件,使它变成PHP脚本文件的一部分。(事实好像并非如此,,以下的代码会正常执行)

<?php

class Mpf_Autoloader {

        public static function autoload($class) {

                require './sub/'.$class.'.php';

        }


        public static function registerAutoload($func = 'Mpf_AutoLoader::autoload', $enable = true) {

                $enable ? spl_autoload_register($func) : spl_autoload_unregister($func);

        }

}

Mpf_Autoloader::registerAutoload(); b::test();



这两种结构除了在如何处理包含失败之外,其他完全一样:在包含失败 时,include()产生一个警告并继续执行,而require()则导致一个致命错误。功能方面并无二致,最好是不作区分。


被引入以后的作用域表现,php与python有点不同。执行引用后,被引入的文件中变量拥有本地作用域,而函数和类拥有全局作用域。


1、被包含文件的变量的PHP include作用域遵从(不改变)包含文件所在处的作用域。

2、所有在被包含文件中定义的函数和类在被包含后,在包含文件里都具有全局作用域。


这样是对的:

require "sub/b.php";

b::test();

echo $var;


而这样是错的

class Mpf_Autoloader {

        public static function autoload($class) {

                require './sub/'.$class.'.php';

        }


        public static function registerAutoload($func = 'Mpf_AutoLoader::autoload', $enable = true) {

                $enable ? spl_autoload_register($func) : spl_autoload_unregister($func);

        }

}

Mpf_Autoloader::registerAutoload();

b::test();

//下一句报错

echo $var;


从入口文件开始,资源要么是就在文件中,要么是显式的require进来,要么就是通过autoload函数加载进来。最后总会汇成一个大文件。



变量生命周期


我们关注生命周期,以确定变量在程序存续期间(进程级别)是否有保持值的能力,即变量是不是静态的。只关注普通的全局变量和局部变量,类中的变量的保持值的能力单独讨论。


基本上全局变量都是静态存储的,因为在该文件中任何地方改变它的值,都能保持。但是如果经过资源引用,改变它的值会不会保持。

php和python有不同的表现。php可以继续保持,但是python不一样,这跟python的import机制和对象传递有关系。

php的引用,跟在一个文件没有区别的。而python则是走的对象引用。所以,改变引入的全局变量以后,值是否保持,跟全局变量的数据类型有关系。


和其他语言不一样,传递参数的时候,python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值’来传递对象。这个在import的过程中引入全局变量的过程也类似。



两个例子,说明值传递,命名空间和import模块。

a.py

dic1 = {‘name':'wangwei'}

b.py

from a import dic1

from c import func_test

print dic1

dic1['home'] = 'heze'

print dic1

func_test()

print dic1

c.py

from a import dic1

def func_test():

        dic1['hobit'] = ‘read'


执行b.py


{'name': 'wangwei'}

{'home': 'heze', 'name': 'wangwei'}

{'home': 'heze', 'name': 'wangwei', 'hobit': ‘read'}


普通局部变量都不是静态的,函数终止,变量就释放了。但php可以使用static关键字定义局部静态变量。python没有静态变量的概念,但可以模拟实现局部静态变量的功能


0 0
原创粉丝点击