相对进口量达十亿次

2024-11-15 08:36:00
admin
原创
16
摘要:问题描述:我来过这里:PEP 328 – 导入:多行和绝对/相对模块、包Python 包:相对导入Python 相对导入示例代码不起作用Python 2.5 中的相对导入Python 中的相对导入Python:禁用相对导入还有许多我没有复制的 URL,一些在 SO 上,一些在其他网站上,当时我以为我很快就能找...

问题描述:

我来过这里:

  • PEP 328 – 导入:多行和绝对/相对

  • 模块、包

  • Python 包:相对导入

  • Python 相对导入示例代码不起作用

  • Python 2.5 中的相对导入

  • Python 中的相对导入

  • Python:禁用相对导入

还有许多我没有复制的 URL,一些在 SO 上,一些在其他网站上,当时我以为我很快就能找到解决方案。

永远重复出现的问题是:我该如何解决这个“尝试在非包中进行相对导入”消息?

ImportError:尝试相对导入,但没有已知父包

我在 pep-0328 上构建了该包的精确副本:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

导入是从控制台完成的。

我确实在相应的模块中创建了名为 spam 和 eggs 的函数。当然,它没有起作用。答案显然在我列出的第 4 个 URL 中,但对我来说都是校友。我访问的其中一个 URL 上有这样的响应:

相对导入使用模块的名称属性来确定模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“main”),则相对导入将被解析为模块是顶级模块,而不管模块在文件系统上的实际位置。

上面的回复看起来很有希望,但对我来说都是象形文字。如何让 Python 不返回“尝试在非包中进行相对导入”?-m据说它有一个涉及的答案。

Python 为何会给出该错误消息?“非软件包”是什么意思?为什么以及如何定义“软件包”?


解决方案 1:

脚本与模块

以下是解释。简而言之,直接运行 Python 文件和从其他地方导入该文件之间存在很大差异。 仅仅知道文件位于哪个目录并不能确定 Python 认为它在哪个包中。 这还取决于您如何将文件加载到 Python 中(通过运行或导入)。

有两种方法可以加载 Python 文件:作为顶级脚本或作为模块。如果您直接执行文件(例如通过python myfile.py在命令行中键入),则文件将作为顶级脚本加载。当import在其他文件中遇到语句时,它将作为模块加载。一次只能有一个顶级脚本;顶级脚本是您运行以启动程序的 Python 文件。

命名

当文件被加载时,会为其指定一个名称(存储在其__name__属性中)。如果它是作为顶级脚本加载的,则其名称为__main__。如果它是作为模块加载的,则其名称为文件名,前面是它所属的任何包/子包的名称,以点分隔。

例如在您的例子中:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

如果你导入了moduleX(注意:导入了,而不是直接执行),它的名字将是package.subpackage1.moduleX。如果你导入了moduleA,它的名字将是package.moduleA。但是,如果你直接 moduleX从命令行运行,它的名字将是__main__,如果你直接moduleA从命令行运行,它的名字将是__main__。当模块作为顶级脚本运行时,它会失去其正常名称,而其名称将是__main__

不通过包含模块的包来访问模块

还有一个问题:模块的名称取决于它是从所在目录“直接”导入的还是通过包导入的。只有当您在目录中运行 Python,并尝试导入同一目录(或其子目录)中的文件时,这才会有所不同。例如,如果您在目录中启动 Python 解释器package/subpackage1,然后执行import moduleX, 的名称moduleX将只是moduleX,而不是package.subpackage1.moduleX。这是因为当以交互方式进入解释器时,Python 会将当前目录添加到其搜索路径中;如果它在当前目录中找到要导入的模块,它将不知道该目录是包的一部分,并且包信息不会成为模块名称的一部分。

一种特殊情况是,如果您以交互方式运行解释器(例如,只需键入python并开始即时输入 Python 代码)。在这种情况下,该交互式会话的名称是__main__

现在,这是错误消息的关键:如果模块的名称没有点,则不将其视为包的一部分。文件在磁盘上的实际位置并不重要。重要的是它的名称是什么,而它的名称取决于您如何加载它。

现在看一下你在问题中引用的话:

相对导入使用模块的名称属性来确定该模块在软件包层次结构中的位置。如果模块的名称不包含任何软件包信息(例如,它被设置为“main”),则相对导入将被解析为该模块是顶级模块,而不管该模块在文件系统上的实际位置。

相对进口...

相对导入使用模块的名称来确定它在包中的位置。当您使用相对导入(如)时from .. import foo,点表示要在包层次结构中提升一定级别的级别。例如,如果您当前的模块名称是package.subpackage1.moduleX,则..moduleA意味着package.moduleA。要使from .. import正常工作,模块名称必须至少包含与语句中一样多的点import

... 仅在包中是相对的

但是,如果您的模块名称为__main__,则它不被视为在包中。其名称中没有点,因此您无法from .. import在其中使用语句。如果您尝试这样做,您将收到“非包中的相对导入”错误。

脚本无法导入相对

您可能尝试从命令行运行或类似操作moduleX。执行此操作时,其名称设置为__main__,这意味着其中的相对导入将失败,因为其名称未表明它位于包中。请注意,如果您从模块所在的同一目录运行 Python,然后尝试导入该模块,也会发生这种情况,因为如上所述,Python 会“过早”在当前目录中找到该模块,而没有意识到它是包的一部分。

还要记住,当您运行交互式解释器时,该交互式会话的“名称”始终是__main__。因此,您不能直接从交互式会话进行相对导入。相对导入仅供在模块文件内使用。

两种解决方案:

  1. 如果你确实想moduleX直接运行,但仍希望将其视为包的一部分,则可以执行python -m package.subpackage1.moduleX-m告诉 Python 将其作为模块加载,而不是作为顶级脚本加载。

  2. 或者也许您实际上并不想运行 moduleX,您只想运行其他脚本,例如myfile.py,该脚本使用中的函数moduleX。如果是这种情况,请将其放在myfile.py 其他地方不是目录内package)并运行它。如果myfile.py您在里面执行类似 的操作from package.moduleA import spam,它将正常工作。

笔记

  • 对于这两种解决方案,包目录(package在您的示例中)必须可以从 Python 模块搜索路径(sys.path)访问。如果不能,您将根本无法可靠地使用包中的任何内容。

  • 自 Python 2.6 起,用于包解析的模块“名称”不仅由其__name__属性决定,还由__package__属性决定。这就是为什么我避免使用显式符号__name__来引用模块的“名称”。自 Python 2.6 起,模块的“名称”实际上是__package__ + '.' + __name__,或者只是__name__如果__package__None。)

解决方案 2:

这确实是 Python 内部的一个问题。造成混淆的原因是人们错误地将相对导入视为路径相对,但事实并非如此。

例如当你在faa.py中写入:

from .. import foo

仅当faa.py在执行期间被python识别并加载为包的一部分时,这才有意义。在这种情况下, faa.py
模块名称将是例如some_packagename.faa。如果文件只是因为位于当前目录中而被加载,则在运行 python 时,其名称不会引用任何包,最终相对导入将失败。

引用当前目录中的模块的一个简单解决方案是使用以下命令:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

解决方案 3:

外语的长答案太多了。所以我会尽量写得简短些。

如果您写的from . import module与您想的相反,module则不会从当前目录导入,而是从包的顶层导入!如果您将.py文件作为脚本运行,它根本不知道顶层在哪里,因此拒绝工作。

如果你像这样py -m package.module从上面的目录启动它package,那么 Python 就知道顶层在哪里。这与 Java 非常相似:java -cp bin_directory package.class

解决方案 4:

因此,在与其他人一起抱怨了这个问题之后,我偶然发现了Dorian B在这篇文章中发布的一条注释,它解决了我遇到的具体问题,即我将开发用于 Web 服务的模块和类,但我还希望能够在编码时使用 PyCharm 中的调试器功能对它们进行测试。要在自包含类中运行测试,我将在类文件的末尾包含以下内容:

if __name__ == '__main__':
   # run test code here...

但是如果我想导入同一文件夹中的其他类或模块,那么我必须将所有导入语句从相对表示法更改为本地引用(即删除点 (.))但在阅读 Dorian 的建议后,我尝试了他的“一行代码”,它成功了!现在我可以在 PyCharm 中进行测试,当我在另一个被测类中使用该类时,或者当我在我的 Web 服务中使用该类时,我可以保留我的测试代码!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

if 语句检查我们是否将此模块作为main运行,或者它是否在另一个作为main测试的模块中使用。也许这是显而易见的,但我在这里提供这条说明,以防其他因上述相对导入问题而感到沮丧的人可以利用它。

解决方案 5:

这是一个我不推荐的解决方案,但在某些模块根本无法生成的情况下可能会有用:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()

解决方案 6:

这是一个通用的配方,经过修改以适合作为示例,我现在用它来处理以包形式编写的 Python 库,其中包含相互依赖的文件,我希望能够逐个测试它们的各个部分。我们调用它lib.foo并假设它需要访问lib.fileA函数f1f2,以及lib.fileBClass3

我添加了几个print调用来帮助说明其工作原理。实际上,您可能希望删除它们(可能还有该from __future__ import print_function行)。

这个特定示例过于简单,无法展示我们何时真正需要将条目插入sys.path。(请参阅Lars 的回答,了解我们确实需要它的情况,即当我们拥有两级或更多级的包目录时,然后我们使用os.path.dirname(os.path.dirname(__file__))— 但在这里它实际上也没有什么坏处。)无需测试即可安全地执行此操作if _i in sys.path。但是,如果每个导入的文件都插入相同的路径(例如,如果fileAfileB都想从包中导入实用程序),这会因sys.path多次使用相同的路径而变得混乱,因此最好if _i not in sys.path在样板中使用 。

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

这里的想法是这样的(请注意,这些在 Python 2.7 和 Python 3.x 中的功能都相同):

  1. 如果作为import libfrom lib import foo作为常规包从普通代码导入运行,__package则为lib__name__lib.foo。我们采用第一个代码路径,从 导入.fileA,等等。

  2. 如果以 方式运行python lib/foo.py__package__则将为 None ,并且__name__将为__main__

我们采用第二个代码路径。lib目录已经存在,sys.path因此无需添加。我们从 导入fileA,等等。

  1. 如果在lib目录中运行python foo.py,则行为与情况 2 相同。

  2. 如果在lib目录中以运行python -m foo,则行为类似于情况 2 和 3。但是,lib目录的路径不在 中sys.path,因此我们在导入之前添加它。如果我们运行 Python 然后 ,情况也是如此import foo

(由于. sys.path,我们实际上不需要在这里添加路径的绝对版本。这是我们想要做的更深的包嵌套结构from ..otherlib.fileC import ...产生影响的地方。如果你不这样做,你可以sys.path完全省略所有的操作。)

笔记

还有一个怪癖。如果你从外部运行整个过程:

python2 lib.foo

或者:

python3 lib.foo

行为取决于 的内容lib/__init__.py。如果 存在且为空,则一切正常:

Package named 'lib'; __name__ is '__main__'

但是如果lib/__init__.py 它本身导入routine以便可以routine.name直接导出为lib.name,则会得到:

python2 lib.foo

输出:

Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

也就是说,模块被导入两次,一次通过包,然后再次通过 as 导入__main__,以便运行您的main代码。Python 3.6 及更高版本对此发出警告:

python3 lib.routine

输出:

Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

警告是新的,但警告行为却不是。这是一些人所说的双重导入陷阱的一部分(有关更多详细信息,请参阅问题 27487。)Nick Coghlan 说:

下一个陷阱存在于所有当前的 Python 版本中,包括 3.3,并且可以总结为以下一般准则:“永远不要将包目录或包内的任何目录直接添加到 Python 路径中”。

请注意,虽然我们在这里违反了该规则,但我们在加载的文件作为包的一部分加载时才这样做,并且我们的修改专门用于允许我们访问该包中的其他文件。 (并且,正如我所指出的,对于单级包,我们可能根本不应该这样做。)如果我们想要更加干净,我们可以将其重写为,例如:

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

也就是说,我们进行sys.path足够长的时间修改以实现我们的导入,然后将其恢复到原来的状态(_i当且仅当我们添加了一份副本时才删除一份副本_i)。

解决方案 7:

@BrenBarn 的回答说明了一切,但如果你和我一样,可能需要一段时间才能理解。以下是我的案例以及 @BrenBarn 的回答如何适用于它,也许它会对你有所帮助。

案件

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

使用我们熟悉的示例,并向其中添加 moduleX.py 具有相对于 ..moduleA 的相对导入。鉴于我尝试在导入 moduleX 的 subpackage1 目录中编写测试脚本,但随后出现了 OP 描述的可怕错误。

解决方案

将测试脚本移至与包相同的级别并导入 package.subpackage1.moduleX

解释

如上所述,相对导入是相对于当前名称进行的。当我的测试脚本从同一目录导入 moduleX 时,moduleX 内的模块名称为 moduleX。当遇到相对导入时,解释器无法备份包层次结构,因为它已经位于顶部

当我从上面导入 moduleX 时,moduleX 内部的名称是 package.subpackage1.moduleX,并且可以找到相对导入

解决方案 8:

我遇到过类似的问题,我不想更改 Python 模块搜索路径,而需要从脚本中相对地加载模块(尽管“脚本不能全部导入相对模块”,正如BrenBarn 很好地解释的那样)。

所以我使用了以下 hack。不幸的是,它依赖于imp自版本 3.4 以来被弃用的模块,该模块被删除以支持importlib。(这也适用于importlib吗?我不知道。)不过,该 hack 目前有效。

从文件夹中的脚本moduleX访问成员的示例:subpackage1`subpackage2`

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

一种更简洁的方法似乎是修改Federico 提到的用于加载模块的 sys.path 。

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *

解决方案 9:

如果您不想执行任何以下操作,这里有一个非常简单的解决方案:

  • 添加__init__.py文件

  • 运行python -m mymodule

  • 编辑 __package__

  • 添加if签到__main__

  • sys.path手工编辑

  • 编辑PYTHONPATH

  • 重组项目

pip 安装 importmonkey

这是 sys.path hack 的强大包装器,可以让一切保持简单和整洁。

[ github ] [ pip ] [ docs ]

├─ src
│   └─ project
│       └─ mymodule.py
└─ test
    └─ test.py
# In test.py

from importmonkey import add_path
add_path("../src/project")  # relative to current __file__
import mymodule

# add as many paths as needed, absolute or relative
# unix path conventions work so you can use '..' and '.'
# add_path validates the paths and returns added path as string

该模块无需__init__.py与其关联即可工作。

隶属关系披露:我制作了 importmonkey。

解决方案 10:

根据 Lars 的建议,我将这种方法包装在一个实验性的新导入库中:ultraimport

它使程序员能够更好地控制导入,并允许基于文件系统的导入。因此,您可以从脚本中进行相对导入。父包不是必需的。ultraimports 始终有效,无论您如何运行代码或当前工作目录是什么,因为 ultraimport 使导入变得明确。您不需要更改 sys.path,也不需要 try/except 块来有时进行相对导入,有时进行绝对导入。

然后你可以在 somefile.py 中写入类似以下内容:

import ultraimport
foo = ultraimport('__dir__/foo.py')

dir 是 somefile.py 的目录,ultraimport() 的调用者。foo.py 将与 somefile.py 位于同一目录中。

导入此类脚本时需要注意的一个问题是,如果它们包含进一步的相对导入。ultraimport 有一个内置预处理器,可以将后续的相对导入重写为 ultraimport,以便它们继续工作。不过,目前此功能有些受限,因为原始 Python 导入不明确,而且您能做的非常有限。

解决方案 11:

__name__根据相关代码是在全局命名空间中运行还是作为导入模块的一部分运行而变化。

如果代码不在全局空间中运行,__name__则为模块的名称。如果它在全局命名空间中运行——例如,如果您将其输入到控制台中,或者使用脚本运行模块,python.exe yourscriptnamehere.py__name__变为"__main__"

您会看到很多 Python 代码用于 if __name__ == '__main__'测试代码是否从全局命名空间运行 - 这允许您拥有一个兼作脚本的模块。

您是否尝试过从控制台进行这些导入?

解决方案 12:

相对导入使用模块的名称属性来确定模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“main”),则相对导入将被解析为模块是顶级模块,而不管模块在文件系统上的实际位置。

我为PyPI编写了一个小型 Python 包,可能对这个问题的查看者有所帮助。如果希望能够在包/项目内运行包含上层包的导入的 Python 文件,而无需直接进入导入文件的目录,则该包可以作为解决方法。

解决方案 13:

对于像我这样可怜的人来说,这个方法根本行不通,以下是FEMista发布的解决方案

添加一个中间父文件夹,作为两个兄弟文件夹的公共分支:

package/    
    __init__.py
    SUBPACKAGES/
        __init__.py
        subpackage1/
            __init__.py
            moduleX.py
            moduleY.py
        subpackage2/
            __init__.py
            moduleZ.py
        moduleA.py

记得将文件夹添加SUBPACKAGESimport路径中。

解决方案 14:

我将展示一个文件夹结构,它可以从同一文件夹、子文件夹和父文件夹进行相对导入。文件结构如下所示:

.
├── main.py
└── bardir/
    ├── __init__.py
    ├── bar.py
    └── foodir/
        ├── __init__.py
        ├── foo.py
        ├── foo2.py
        └── bazdir/
            ├── __init__.py
            └── baz.py

选择此文件夹结构,因此foo.py首先调用它并按顺序显示 foo、foo2、bar 和 baz。同时显示相对导入的三种方式。

内容main.py

print('__name__:',__name__)
print('__package__:', __package__)
print('main')
from bardir.foodir import foo

内容foo.py

print('        __name__:',__name__)
print('        __package__:', __package__)
print('        foo')

from . import foo2
from .. import bar
from . bazdir import baz

每个__init__文件都是空的,其他文件包含类似的打印语句。运行时,main.py您将获得以下输出:

__name__: __main__
__package__: None
main
        __name__: bardir.foodir.foo
        __package__: bardir.foodir
        foo
        __name__: bardir.foodir.foo2
        __package__: bardir.foodir
        foo2
    __name__: bardir.bar
    __package__: bardir
    bar
            __name__: bardir.foodir.bazdir.baz
            __package__: bardir.foodir.bazdir
            baz

您可以使用以下规则来推理此类文件结构:

  1. 相对导入是相对于包变量进行的。一个点表示相对于 进行查看__package__。两个点表示相对于第一个点(从右侧)之前的包进行查看。三个点表示相对于第二个点之前的包进行查看。__package__不包含点?您无法使用..,并且会收到错误。您的__package__等于 吗None?您无法进行相对导入!

  2. 只要有__init__.py文件,文件夹就会变成包。init 文件可能为空。

  3. 如果直接运行脚本,即按下 IDE 中的运行按钮,变量__name__将被设置为__main____package__变量将被设置为None。这意味着使用点的相对导入将不起作用。

解决方案 15:

大多数情况下,当我看到ValueError: attempted relative import beyond top-level package并拔掉头发时,解决方案如下:

您需要在文件层次结构中迈向更高的级别!

#dir/package/module1/foo.py

#dir/package/module2/bar.py
from ..module1 import foo

bar.py尽管导入过程从未超出当前目录,但在启动解释器时进行导入仍会dir/package/导致错误。

bar.py当解释器启动时导入就会dir/成功。

对于单元测试来说也是如此:
python3 -m unittest discover --start-directory=.从 可以成功运行dir/,但从 则不行dir/package/

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用