您的位置:首页 > 编程语言 > Python开发

python和php的资源引用

2015-11-18 19:52 585 查看
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__都不会生效。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: