在python中以正确的方式从子包内部调用函数 [英] Calling a function from inside a sub-package the correct way in python

查看:93
本文介绍了在python中以正确的方式从子包内部调用函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直试图了解如何从 python 的子包内部正确调用函数.我希望能够以我调用的方式调用函数,例如,os 包中的函数 isfile,使用 os.path.isfile().我用这样的结构制作了一个测试包:

I have been trying to understand how to properly call a function from inside a subpackage in python. I wanted to be able to call the function the way I call, for example, function isfile from os package, with os.path.isfile(). I made a test package with a structure like this:

sandbox/
    -- __init__.py
    -- acid.py
    -- pack1/
        -- __init__.py
        -- fly/
            -- __init__.py
            -- plane.py
        -- by/
    -- pack2/

那里只有两个模块,acid.pyplane.py.它们都只包含一个函数,例如plane.py

There are only two modules there, acid.py and plane.py. Both of them contain just a function, e.g. plane.py is

"""plane module"""
def plane(x):
    x=x+4
    return x

要在我的 test.py 代码中使用该函数,我将

To use the function in my test.py code, I put

import pack1

沙箱/__init__.py

import fly 

sandbox/pack1/__init__.py

from plane import plane

sandbox/pack1/fly/__init__.py

当时的测试代码是:

import sandbox
print sandbox.pack1.fly.plane(3)

这是从子包导入函数的正确方法,还是我误解了一些东西?

Is this the right way to import a function from a subpackage, or I'm misunderstanding things?

推荐答案

你所做的当然是有效的,尽管有一些值得做出的改变.

What you did certainly works, although there are several worthwhile changes to make.

首先,关于从包导入的说明:导入模块在语义上与访问模块中的内容不同,即使 from xml import saxfrom datetime import date在语法上是等价的.不可能只导入模块的部分,因此

First, a note about importing from packages: importing a module is semantically distinct from accessing something in a module, even though from xml import sax and from datetime import date are syntactically equivalent. It's impossible to import only part of a module, so that

import datetime
datetime.date.today()  # OK: date is a class

保证工作.但是,可以导入包但不能导入它包含的模块.这对效率来说是件好事,但这确实意味着

is guaranteed to work. However, it is possible to import a package but not the modules it contains. This is a good thing for efficiency, but it does mean that

import xml
xml.sax.parse(...)     # AttributeError: 'module' object has no attribute 'sax'

是一个错误.不幸的是,此类错误通常未被发现,因为某些其他代码已经导入了 sax,使其可用于导入 xml 的任何其他代码.(from xml import sax 中的from"一词指的是磁盘上的完整包,而不是它存储的模块对象 xml — 新模块作为一个属性!)

is an error. Unfortunately, such errors often go uncaught because some other code has already imported sax, making it available to any other code that imports xml. (The word "from" in from xml import sax is referring to the complete package on disk, not the module object xml — on which it stores the new module as an attribute!)

顺便说一句,请注意您的 os.path 示例是一个异常:写作

As an aside, note that your example of os.path is an abberation: writing

    import os
    os.path.isfile(...)

有效,但仅仅是因为 os.path 实际上不是一个模块,而是 posixpathntpath 之一的别名等.(然后它会安装在 sys.modules 中以 allow import os.path 就像它是一个普通模块一样.)

works, but only because os.path is not actually a module but an alias for one of posixpath, ntpath, etc. (It then gets installed in sys.modules to allow import os.path as if it were a normal module.)

因此,对于一个包,有一组用户必须知道的公共模块(因为它们必须按名称导入才能使用);其余的是内部模块,包在必要时自行加载.如果一个包不包含公共模块,它是一个包(例如,importlib 和它的 一个公共函数实际上是作为一个包实现的,以便与 Python 3 向前兼容.

As such, for a package there is a set of public modules that the user must be aware of (because they must be imported by name to be available); the rest are internal modules that the package loads itself when necessary. If a package contains no public modules, it is irrelevant to the user that it is a package (for example, importlib with its one public function is actually implemented as a package for forward compatibility with Python 3).

现在是建议:

  1. 隐式相对导入已弃用:从 .例如,在 sandbox/__init__.py 中导入 pack1 而不是 import pack1.
  2. from plane import plane(或from .plane import plane,遵循上述观点)是有问题的,因为它覆盖对模块 plane.py 与函数的引用.反而:
  1. Implicit relative imports are deprecated: write from . import pack1 instead of just import pack1 in sandbox/__init__.py, for instance.
  2. The from plane import plane (or from .plane import plane, following the above point) is problematic because it overwrites the reference to the module plane.py with a reference to the function. Instead:
  1. 直接在包的 __init__.py 中定义用户可见的入口点(如 plane()),根据需要从私有模块导入内部函数,或立>
  2. 重命名模块(为 plane_module.py 左右)以避免冲突.
  1. Define the user-visible entry points (like plane()) directly in their package's __init__.py, importing internal functions from private modules as needed, or
  2. Rename the module (to plane_module.py or so) to avoid the collision.

  • 然而,无论如何,让包自动导入其公共模块通常不是一个好主意:它迫使客户端为加载包中未使用的部分付费,并且模糊了公共模块和简单嵌套模块之间的区别名称.相反,编写像

  • However, it's not generally a good idea to have a package automatically import its public modules anyway: it forces the client to pay for loading unused parts of the package, and it blurs the distinction between public modules and simple nested names. Instead, write client code like

    import sandbox.pack1.fly
    print sandbox.pack1.fly.plane(3)    # the same line you had
    

    from sandbox.pack1 import fly
    print fly.plane(3)
    

    如果你想避免重复sandbox.pack1.

    通常建议 __init__.py 完全为空,并且在 Python 3.3 中可以定义完全没有任何 __init__.py 的包(这必然会使它变空").此策略的作用不仅仅是不自动导入"建议,因为它阻止从私有模块(如 plane)加载内容.

    It is often suggested that __init__.py be entirely empty, and in Python 3.3 it became possible to define packages without any __init__.py at all (which by necessity "makes it empty"). This policy does more than the "no automatic import" suggestion in that it precludes loading things from private modules (like plane).

    有时有很好的理由拥有一个非空的 __init__.py;例如,它允许在不破坏其客户端的情况下将现有模块重新组织成一个包.我个人认为没有理由特别限制其内容;如需进一步讨论,请参阅__init__.py 的用途是什么?.

    There are sometimes good reasons to have a non-empty __init__.py; for example, it allows reorganzing an existing module into a package without breaking its clients. I personally see no reason to especially restrict its contents; for further discussion see What is __init__.py for?.

    这篇关于在python中以正确的方式从子包内部调用函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆