关闭

python和php的资源引用

213人阅读 评论(0) 收藏 举报
分类:

 php和python的资源引用

关于在引用机制上的不同,在讨论作用域和生命周期中已经有讨论,机制的不同导致使用上的不同直接表现在下面的例子:


a文件:

require "b.php";

function func_a(){

        echo "i am in a ";

}

func_b();


b文件

<?php function func_b(){

        func_a();


可以看出,文件包含式的引用 只要资源被引用过 或者已经存在于入口文件的全局作用域中。可以直接使用。而python则是把文件作为module放在字典中,每个文件的名字空间都是独立的,每个文件中要使用的资源,都必须显式的引用,而且不存在嵌套引用,即a引用了b,c引用了a,也不能用b。


本篇主要讨论功能和使用。只是对比着记忆。从基本用法、路径设置、资源自动加载。



PHP的require与include


有require和include两种方式。都是函数,也可以当做语法来用,后接字符串,可以用在程序的任何地方,即不加括号也行。以下两种方式都是正确的。

require “a.php”;

require(“a.php”);


关于require与include的区别,只要记住一点,功能上是一样的:


include引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。

PHP Warning:  include(): Failed opening 'd.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in /home/wang.wei_890/php_test/a.php on line 2

require引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。

PHP Fatal error:  require(): Failed opening required 'd.php' (include_path='.:/usr/share/pear:/usr/share/php') in /home/wang.wei_890/php_test/a.php on line 2


include_once和require_once的功能是引入前检查当前进程是否已经引入过。如果已经引入过,则没有任何动作。而python的重复判断是自动进行的。之前的讨论中曾提到,


require的资源中的全局变量只在本地作用域生效(类和函数中都是全局生效的,常量当然也是)。

python资源都是本地作用域生效的。


就会有这样一种情形,在局部作用域中引用了,在外部使用_once引用,就完了,用不了这个全局变量了。(当然没谁会这么用,第一次在全局作用域中require就行了)


1.php:


$abc = "1var";

define(“NAME","wangwei");


a.php:


function func(){

        require "1.php";

        echo $abc;

        echo NAME;

}

func();

require_once “./1.php”;

//会报错

//echo $abc;

//不会报错

echo NAME;


PHP的路径搜索与搜索路径设置


相对路径 相对路径指以.开头的路径,例如

./a/a.php (相对当前目录)    

../common.inc.php (相对上级目录)


绝对路径  绝对路径是以 / 开头或者windows下的 C:/ 类似的盘符开头的路径,全路径不用任何参考路径就可以唯一确定文件的最终地址。 例如

/apache/wwwroot/site/a/a.php

c:/wwwroot/site/a/a.php


未确定路径

凡是不以 . 或者 / 开头、也不是windows下 盘符:/ 开头的路径,例如

复制代码 代码如下:


a/a.php  

common.inc.php,

开始以为这也是相对路径,但在php的include/require包含机制中,这种类型的路径跟以 . 开头的相对路径处理是完全不同的。require './a.php' 和 require 'a.php' 是不同的


下面分析这三种类型包含路径的处理方式:首先记住一个结论:如果包含路径为相对路径或者绝对径,则不会到include_path去查找



测试环境说明

/app/test/a.php,讨论是针对直接访问A情况


相对路径:

相对路径需要一个参考目录才能确定文件的最终路径,在包含解析中,不管包含嵌套多少层,这个参考目录是程序执行入口文件所在目录(即工作目录)。

示例1

A中定义  require './b/b.php';  // 则B=[SITE]/app/test/b/b.php

B中定义  require './c.php';    // 则C=[SITE]/app/test/c.php 不是[SITE]/app/test/b/c.php

示例2

A中定义  require './b/b.php';  // 则B=[SITE]/app/test/b/b.php 

B中定义  require '../c.php';   // 则C=[SITE]/app/c.php  不是 [SITE]/app/test/c.php


绝对路径

绝对路径的比较简单,不容易混淆出错,require的就是对应磁盘中的文件

require '/wwwroot/xxx.com/app/test/b.php';    // Linux中

dirname(__FILE__)计算出来的也是一个绝对路径形式的目录,但是要注意__FILE__是一个Magic constants,不管在什么时候都等于写这条语句的php文件所在的绝对路径,因此dirname(__FILE__)也总是指向写这条语句的php文件所在的绝对路径,跟这个文件是否被其他文件包含使用没有任何关系。


示例1

A中定义  require '../b.php';                  // 则B=[SITE]/app/b.php

B中定义  require dirname(__FILE__).'/c.php';  // 则B=[SITE]/app/c.php


4. 未确定路径

首先在逐一用include_path中定义的包含目录来拼接[未确定路径],找到存在的文件则包含成功退出,如果没有找到,则用执行require语句的php文件所在目录来拼接[未确定路径]组成的全路径去查找该文件,如果文件存在则包含成功退出,否则表示包含文件不存在,出错。 未确定路径比较容易搞混不建议使用。

5. 解决方案

由于“相对路径”中的“参照目录”是执行入口文件所在目录,“未确定”路径也比较容易混淆,因此最好的解决方法是使用“绝对路径”; 


设置未确定路径的自动搜索路径


set_include_path

 


$path = '/usr/lib/pear';

set_include_path(get_include_path() . PATH_SEPARATOR . $path);


PHP类自动加载


自动加载一般依赖于 

1、机制:即在当前命名空间找不到一个类时,可以“触发某个设置好的处理方法”来加载。

2、类的命名规则拼出一个未确定路径,然后凑成一个绝对路径直接require,或者使用自动搜索路径去搜索。还依赖,文件的命名规则。如:类A_B_C,文件名称成C。


有两种方法 :__autoload魔术方法和 spl_autoload_register


function __autoload($class_name){

        require_once "./".str_replace('_', '/', $class_name) . ".php";

}

$obj =new sub_a();



class Mpf_Autoloader {

/**

* 使用默认autoload方法只用于搜索APP_DIR目录下的controllers和models

* @param string $class

* @return bool

*/

public static function autoload($class) {

$class = strtolower($class);

$file = CODE_BASE_DIR . '/' . str_replace('_', '/', $class) . '.php';


if (is_file($file)) {

require $file;

}


return (class_exists($class, false) || interface_exists($class, false));

}


/**

* Register autoload function

*

* @param string $func

* @param boolean $enable

*/

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

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

}

}


区别:


__autoload的最大缺陷是无法有多个autoload方法 


好了, 想下下面的这个情景,你的项目引用了别人的一个项目,你的项目中有一个__autoload,别人的项目也有一个__autoload,这样两个__autoload就冲突了。解决的办法就是修改__autoload成为一个,这无疑是非常繁琐的。 


因此我们急需使用一个autoload调用堆栈,这样spl的autoload系列函数就出现了。你可以使用spl_autoload_register注册多个自定义的autoload函数 


如果你的PHP版本大于5.1的话,你就可以使用spl_autoload 

也就是spl方法可以注册多个自动处理函数,它会按顺序逐个调用加载器。。__autoload 已经不被推荐使用了,推荐使用 spl_autoload_register 来注册加载器




共存:


如果已经使用了 spl_autoload_register, 那么 __autoload 会被忽略,除非你手动重新注册它。




Python的import与__import__


关于import的机制之前有过讨论。就是将文件作为模块加载到sys.module中。然后把模块或者模块中的可用属性添加到名称空间。


有下面几种使用形式:

import a 

from a import var

from a import * 


from module import * 语句只能用于一个模块的最顶层.*特别注意*:由于存在作用域冲突,不允许在函数中使用from 语句。


每个模块都拥有 __name__ 属性,它是一个内容为模块名字的字符串。最顶层的模块名称是 __main__ .命令行或是交互模式下程序都运行在__main__ 模块内部. 利用__name__属性,我们可以让同一个程序在不同的场合(单独执行或被导入)具有不同的行为,象下面这样做:


# 检查是单独执行还是被导入

if __name__ == '__main__':

      # Yes

      statements

else:

      # No (可能被作为模块导入)

      statements 


同import语句同样的功能,但__import__是一个函数,并且只接收字符串作为参数,所以它的作用就可想而知了。其实import语句就是调用这个函数进行导入工作的,import sys <==>sys = __import__('sys')



说明:

通常在动态加载时可以使用到这个函数,最常见的场景就是插件功能的支持。


Python的路径搜索与搜索路径设置


python的引用模块的时候除了机制跟php完全不同,python引用的模块(可能是字符串形式)必须是模块名,不能是带有路径名的形式。


所有的引用都从sys.path的路径列表中依次寻找对应的文件,空字符串是当前

>>> import sys

>>> sys.path

['', '/usr/lib/python2.6/site-packages/meld3-1.0.0-py2.6.egg', '/usr/lib/python2.6/site-packages/memory_profiler-0.36-py2.6.egg', '/usr/lib/python2.6/site-packages/motop-4.2-py2.6.egg', '/usr/lib/python2.6/site-packages/argparse-1.4.0-py2.6.egg', '/usr/lib64/python26.zip', '/usr/lib64/python2.6', '/usr/lib64/python2.6/plat-linux2', '/usr/lib64/python2.6/lib-tk', '/usr/lib64/python2.6/lib-old', '/usr/lib64/python2.6/lib-dynload', '/usr/lib64/python2.6/site-packages', '/usr/lib64/python2.6/site-packages/gtk-2.0', '/usr/lib/python2.6/site-packages', ‘/usr/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg-info']


可以通过sys.path.append来增加搜索路径。查找的文件不限于.py文件。


当查询模块 foo 时(import foo/from foo...),解释器按照 sys.path 列表中目录顺序来查找以下文件(目录也是文件的一种):

定义为一个包的目录 foo

foo.so, foomodule.so, foomodule.sl,或 foomodule.dll (已编译扩展)

foo.pyo (只在使用 -O 或 -OO 选项时)

foo.pyc

foo.py


那么问题来了,什么是包呢

python的包


如果a目录下有一个src.py,b目录下也有一个src.py,a和b,想引用它们,于是都在sys.path中添加了,但系统只能按次序搜索,它也不知道我想用哪个。


多个关系密切的模块应该组织成一个包,以便于维护和使用。这项技术能有效避免名字空间冲突。创建一个名字为包名字的文件夹并在该文件夹下创建一个__init__.py 文件就定义了一个包。你可以根据需要在该文件夹下存放资源文件、已编译扩展及子包





Graphics/

      __init__.py

      Primitive/

         __init__.py

         lines.py

         fill.py

         text.py

         ...

      Graph2d/

         __init__.py

         plot2d.py

         …


包的使用方式:

import Graphics.Primitive.fill 导入模块Graphics.Primitive.fill,只能以全名访问模块属性,例如 Graphics.Primitive.fill.floodfill(img,x,y,color).


from Graphics.Primitive import fill 导入模块fill ,只能以 fill.属性名 这种方式访问模块属性,例如 fill.floodfill(img,x,y,color).


from Graphics.Primitive.fill import floodfill 导入模块fill ,并将函数floodfill放入当前名称空间,直接访问被导入的属性,例如 floodfill(img,x,y,color).


无论一个包的哪个部分被导入, 在文件__init__.py中的代码都会运行.这个文件的内容允许为空,不过通常情况下它用来存放包的初始化代码。导入过程遇到的所有 __init__.py文件都被运行.因此 import Graphics.Primitive.fill 语句会顺序运行 Graphics 和 Primitive 文件夹下的__init__.py文件.

__init__文件就是对应的目录名模块

从sub.modules可见一斑:'sub': <module 'sub' from ‘/home/wang.wei_890/py_test/sub/__init__.py'>


既然是包,那么我就得有办法拿到包里所有的模块,自然而然的想到

1、import 包

2、from 包 import *

但很遗憾,这个方法只是将涉及的包/子包对应的__init__.py文件按顺序执行了一遍。没有引入任何模块。

针对两种情况,分别提供了两种解决方案:

├── a.py

└── sub

    ├── b.py

    ├── b.pyc

    ├── __init__.py


1、既然会执行init那就利用它,在init.py中执行import b。a中import sub之后,就可以通过sub.b拿到模块对象

此种解包方法,队任何沿途__init__都生效

import sub.sub_sub

print sub.b

print sub.sub_sub.c


2、__all__ = [“lines","text","fill",...] 把模块名字列表__all__写入init文件。就可以通过from xxx.yyy.zzz import *导入 zzz下面所有的模块。

此种解包方法值对最后一个子包有效,沿途任何__all__都不会生效。







0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:14336次
    • 积分:363
    • 等级:
    • 排名:千里之外
    • 原创:23篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类