暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

每天5分钟玩转Python(17) - 模块和包

飞污熊 2019-11-18
148

模块和包都是用来组织代码用的,在python中一个模块就是一个.py文件, 而一个包就是一个包含了 __init__.py
的文件夹。使用模块最大的好处就是提高代码可维护性,我们在编写代码的时候通常会引用内置模块或第三方模块。

引入包是为了解决命名冲突问题,你可以把包当成是命名空间, 比如你写的 abc.py
模块和其他人写的 abc.py
模块只要在不同的包中就不会冲突。只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突。

比如我现在有这样一个目录结构:

  1. winhong/

  2. web/

  3. __init__.py

  4. util.py

  5. __init__.py (定义cool()函数)

  6. main.py (定义aa()函数和User类)

每一个包目录下面都会有一个 __init__.py
的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录, 而不是一个包。 __init__.py
可以是空文件,也可以有Python代码, __init__.py
本身就是一个模块, 而它的模块名就是所在文件夹名,比如 winhong
目录下的 __init__.py
模块名就是 winhong
, 而web目录下的 __init__.py
的模块名就是 web

注:自己编写模块时候

  1. 模块名不要跟内置模块名冲突,例如系统自带了 sys
    模块,自己的模块就不可命名为 sys.py

  2. 模块内函数与变量名称不要和内置函数名冲突, 点击http://docs.python.org/3/library/functions.html查看Python的所有内置函数

我截了个最新的内置函数图:

导入模块

要使用模块就需要先导入

  1. #!/usr/bin/env python

  2. # -*- coding: utf-8 -*-

  3. """

  4. 这里是模块说明

  5. """

  6. __author__ = "Xiong Neng"


  7. import sys

  8. def demo():

  9. print('\n'.join(sys.path))


  10. if __name__ == '__main__':

  11. demo()

上面第一行是解释器路径,这个是标准写法,第二行指定源文件编码utf-8,这样可支持中文。接下来是模块文档注释,任何模块代码的第一个字符串都被视为模块的文档注释。下面的 __author__="Xiong Neng"
指定了作者,你公开源码后别人可看到。接下来是主体部分,这个是一个模块的标准写法。

注意到:

  1. if __name__ == '__main__':

  2. demo()

当我们直接运行这个模块时候,Python解释器把一个特殊变量 __name__
置为 __main__
, 而如果在其他地方导入该 hello
模块时,if判断将失败,所以这个是模块测试的标准写法。

模块搜索路径

当我们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件,如果找不到就会报错。默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:

  1. import sys

  2. print('\n'.join(sys.path))

如果我们要添加自己的搜索目录,有两种方法:

第一种方法是直接修改 sys.path
,添加要搜索的目录,这种方法是在运行时修改,运行结束后失效:

  1. import sys

  2. sys.path.append('/Users/xiongneng/my_module_dir')

第二种方法是设置环境变量 PYTHONPATH
,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。

私有属性

一般我们会在模块中定义很多函数和变量,但是我们只想公开一部分,那么我们可使用单下划线_来实现。

类似 __xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途, 比如上面的 __author__
, __name__
就是特殊变量,我们没事就别在模块中定义这些东西了。

在模块中:

  1. 单下划线_表示这个属性和函数是私有的,不应该直接访问

  2. 对于 frommoduleimport*
    这样的导入,不管单下划线还是双下划线都不会被导入

  3. 对于 importmodule
    这样的导入都可以使用,但不建议使用,PEP8会告警调用单下划线_

总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠约定和自觉。

特殊属性

对于模块、包、类、对象,函数等,它们都有各自一些特殊的内置属性值,我这里选几个比较有用的讲一下:

  1. __package__
    表示包名

  2. __module__
    表示模块名

  3. __name__
    表示名字

  4. __file__
    表示py文件路径

  5. __dir__
    是该对象中所有属性集合

现在我有这样一个结构:

  1. samples/

  2. __init__.py

  3. def cool(): pass

  4. main.py

  5. def aa(): pass

  6. class User(object): pass

然后我来对这些对象做一个测试,注意我注释掉的是不支持属性并给出报错信息:

  1. # 打印python解释器路径和PYTHONPATH

  2. import sys


  3. print(sys.executable)

  4. print('------------------------分割线----------------------------')


  5. print('\n'.join(sys.path))

  6. print('------------------------分割线----------------------------')


  7. # aa是一个模块函数

  8. from samples.main import aa

  9. print("type={}".format(type(aa)))

  10. # AttributeError: 'function' object has no attribute '__package__'

  11. # print("aa.__package__={}".format(aa.__package__))

  12. print("aa.__module__={}".format(aa.__module__))

  13. print("aa.__name__={}".format(aa.__name__))

  14. # AttributeError: 'function' object has no attribute '__file__'

  15. # print("aa.__file__={}".format(aa.__file__))

  16. print('------------------------分割线----------------------------')


  17. # main是一个模块

  18. import samples.main as main

  19. print("type={}".format(type(main)))

  20. print("init.__package__={}".format(main.__package__))

  21. # AttributeError: 'module' object has no attribute '__module__'

  22. # print("init.__module__={}".format(main.__module__))

  23. print("init.__name__={}".format(main.__name__))

  24. print("init.__file__={}".format(main.__file__))

  25. print('------------------------分割线----------------------------')


  26. # samples是一个模块,同时也是一个包

  27. import samples

  28. print("type={}".format(type(samples)))

  29. print("samples.__package__={}".format(samples.__package__))

  30. # AttributeError: 'module' object has no attribute '__module__'

  31. # print("samples.__module__={}".format(samples.__module__))

  32. print("samples.__name__={}".format(samples.__name__))

  33. print("samples.__file__={}".format(samples.__file__))

  34. print('------------------------分割线----------------------------')


  35. # 导入包samples的__init__模块看看

  36. import samples.__init__ as sample_init

  37. print("type={}".format(type(sample_init)))

  38. print("sample_init.__package__={}".format(sample_init.__package__))

  39. # AttributeError: 'module' object has no attribute '__module__'

  40. # print("sample_init.__module__={}".format(sample_init.__module__))

  41. print("sample_init.__name__={}".format(sample_init.__name__))

  42. print("sample_init.__file__={}".format(sample_init.__file__))

  43. print('------------------------分割线----------------------------')


  44. # 最后打印samples和sample_init的dir(),看看是否不一样

  45. print(dir(samples)) # 多了__init__,__path__,main这三个属性

  46. print(dir(sample_init))

  47. print('------------------------分割线----------------------------')


  48. # 类的导入

  49. from samples.main import User

  50. print("type={}".format(type(User)))

  51. # AttributeError: type object 'User' has no attribute '__package__'

  52. # print("User.__package__={}".format(User.__package__))

  53. print("User.__module__={}".format(User.__module__))

  54. print("User.__name__={}".format(User.__name__))

  55. # AttributeError: type object 'User' has no attribute '__file__'

  56. # print("User.__file__={}".format(User.__file__))


  57. print('------------------------分割线----------------------------')

  58. # 对象

  59. user = User("XiongNeng")

  60. print("type={}".format(type(user)))

  61. # AttributeError: 'User' object has no attribute '__package__'

  62. # print("user.__package__={}".format(user.__package__))

  63. print("user.__module__={}".format(user.__module__))

  64. # AttributeError: 'User' object has no attribute '__name__'

  65. # print("user.__name__={}".format(user.__name__))

  66. # AttributeError: 'User' object has no attribute '__file__'

  67. # print("user.__file__={}".format(user.__file__))


  68. print('------------------------分割线----------------------------'



文章转载自飞污熊,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论