标签归档:module

-m开关的作用是什么?

问题:-m开关的作用是什么?

你能给我解释一下打电话之间有什么区别

python -m mymod1 mymod2.py args

python mymod1.py mymod2.py args

看来在这两种情况下mymod1.py被调用,sys.argv

['mymod1.py', 'mymod2.py', 'args']

那么,该-m开关是做什么用的呢?

Could you explain to me what the difference is between calling

python -m mymod1 mymod2.py args

and

python mymod1.py mymod2.py args

It seems in both cases mymod1.py is called and sys.argv is

['mymod1.py', 'mymod2.py', 'args']

So what is the -m switch for?


回答 0

PEP 338Rationale部分的第一行说:

Python 2.4添加了命令行开关-m,以允许使用Python模块命名空间定位模块以作为脚本执行。激励性的示例是标准库模块,例如pdb和profile,并且Python 2.4实现对于此有限的目的是合适的。

因此,您可以通过这种方式在Python的搜索路径中指定任何模块,而不仅仅是当前目录中的文件。您是正确的,python mymod1.py mymod2.py args其效果完全相同。本Scope of this proposal节的第一行指出:

在Python 2.4中,将执行使用-m定位的模块,就像在命令行中提供了其文件名一样。

还有-m更多的可能,例如使用作为包装一部分的模块等,这就是PEP 338其余部分的意义。阅读以获取更多信息。

The first line of the Rationale section of PEP 338 says:

Python 2.4 adds the command line switch -m to allow modules to be located using the Python module namespace for execution as scripts. The motivating examples were standard library modules such as pdb and profile, and the Python 2.4 implementation is fine for this limited purpose.

So you can specify any module in Python’s search path this way, not just files in the current directory. You’re correct that python mymod1.py mymod2.py args has exactly the same effect. The first line of the Scope of this proposal section states:

In Python 2.4, a module located using -m is executed just as if its filename had been provided on the command line.

With -m more is possible, like working with modules which are part of a package, etc. That’s what the rest of PEP 338 is about. Read it for more info.


回答 1

值得一提的是,只有在程序包具有文件的情况下__main__.py,此方法才有效。否则,该程序包无法直接执行。

python -m some_package some_arguments

python解释器将__main__.py在包路径中查找要执行的文件。等效于:

python path_to_package/__main__.py somearguments

它将在以下时间执行内容:

if __name__ == "__main__":

It’s worth mentioning this only works if the package has a file __main__.py Otherwise, this package can not be executed directly.

python -m some_package some_arguments

The python interpreter will looking for a __main__.py file in the package path to execute. It’s equivalent to:

python path_to_package/__main__.py somearguments

It will execute the content after:

if __name__ == "__main__":

回答 2

在我看来,尽管已经多次询问并回答了这个问题(例如,在这里在这里在这里在这里),但是没有一个现有的答案可以完全或简洁地捕捉到该-m标志的所有含义。因此,以下将尝试改进之前的内容。

简介(TLDR)

-m命令执行了很多操作,并非始终需要所有这些命令。简而言之:(1)允许通过模块名而不是文件名执行python脚本(2)允许选择要添加到的目录以sys.path进行import解析,(3)允许从命令行执行具有相对导入的python脚本。

初赛

为了解释-m标志,我们首先必须弄清楚一些术语。

首先,Python的主要组织单位称为模块。模块有两种形式之一:代码模块和包模块。代码模块是包含python可执行代码的任何文件。软件包模块是包含其他模块(代码模块或软件包模块)的目录。代码模块的最常见类型是*.py文件,而软件包模块的最常见类型是包含__init__.py文件的目录。

其次,可以通过两种不同的方式唯一标识所有模块:<modulename><filename>。模块通常由Python代码中的模块名称(例如import <modulename>)和命令行上的文件名(例如)来标识python <filename>。所有Python解释器都可以通过一组定义良好的规则将模块名转换为文件名。这些规则取决于sys.path变量,因此可以通过更改此值来更改映射(有关如何完成此操作的更多信息,请参阅PEP 302)。

第三,所有模块(代码和程序包)都可以执行(这意味着与模块关联的代码将由Python解释器评估)。根据执行方法和模块类型的不同,对哪些代码进行评估以及何时修改可能会有所不同。例如,如果一个人通过执行一个包模块,python <filename>那么<filename>/__init__.py它将被评估,然后是<filename>/__main__.py。另一方面,如果一个人通过执行相同的程序包模块,import <modulename>那么__init__.py将仅执行程序包。

的历史发展 -m

-m标志最初是在Python 2.4.1中引入的。最初,它的唯一目的是提供一种识别要执行的python模块的替代方法。也就是说,如果我们同时知道模块的<filename><modulename>,则以下两个命令是等效的:python <filename> <args>python -m <modulename> <args>。另外,根据PEP 338,此迭代-m仅适用于顶级模块名称(即,可以直接在sys.path上找到的模块,而无需任何中间包)。

随着完成PEP 338-m功能扩展到支持<modulename>超出顶层modulenames表示。这意味着http.server现在已经完全支持诸如这样的名称。此增强功能还意味着模块中的所有软件包现在都已加载(即,所有软件包__init__.py文件均已评估)。

PEP 366-m带来了最终的主要功能增强。通过此更新,不仅可以支持绝对导入,还可以支持显式相对导入。这是通过修改命令中命名模块的变量来实现的。-m__package__-m

用例

-m标志有两种值得注意的用例:

  1. 从命令行执行可能不知道其文件名的模块。该用例利用了Python解释器知道如何将模块名转换为文件名这一事实。当要从命令行运行stdlib模块或第三方模块时,这特别有利。例如,很少有人知道http.server模块的文件名,但大多数人确实知道其模块名,因此我们可以使用从命令行执行它python -m http.server

  2. 要执行包含绝对导入的本地软件包,而无需安装它。PEP 338中详细介绍了该用例,并利用了将当前工作目录添加到sys.path而不是模块目录的事实。该用例与pip install -e .在开发/编辑模式下安装软件包非常相似。

缺点

经过-m多年的改进,它仍然存在一个主要缺点-它只能执行以python编写的代码模块(即* .py)。例如,如果-m用于执行C编译代码模块,则会产生以下错误,No code object available for <modulename>(请参见此处以获取更多详细信息)。

详细比较

通过python命令执行模块的效果(即python <filename>):

  • sys.path 修改为包括最终目录 <filename>
  • __name__ 设定为 '__main__'
  • __package__ 设定为 None
  • __init__.py 不评估任何软件包(包括其自身的软件包模块)
  • __main__.py评估包装模块;对代码进行代码模块评估。

通过import语句(即import <modulename>)执行模块的影响:

  • sys.path以任何方式修改
  • __name__ 设置为的绝对形式 <modulename>
  • __package__ 设置为中的直接父包 <modulename>
  • __init__.py 针对所有软件包进行评估(包括针对软件包模块的评估)
  • __main__.py评价包模块; 对代码进行代码模块评估

通过-m标志(即python -m <modulename>)执行模块的影响:

  • sys.path 修改为包括当前目录
  • __name__ 设定为 '__main__'
  • __package__ 设置为中的直接父包 <modulename>
  • __init__.py 针对所有软件包进行评估(包括针对软件包模块的评估)
  • __main__.py评估包装模块;对代码进行代码模块评估

结论

-m最简单的角度来看,该标志是使用模块名而不是文件名从命令行执行python脚本的一种方法。另外,-m提供了附加功能,结合了import语句的功能(例如,支持显式相对导入和自动包__init__评估)和python命令行的便利性。

Despite this question having been asked and answered several times (e.g., here, here, here, and here) in my opinion no existing answer fully or concisely captures all the implications of the -m flag. Therefore, the following will attempt to improve on what has come before.

Introduction (TLDR)

The -m flag does a lot of things, not all of which will be needed all the time. In short it can be used to: (1) execute python code from the command line via modulename rather than filename (2) add a directory to sys.path for use in import resolution and (3) execute python code that contains relative imports from the command line.

Preliminaries

To explain the -m flag we first need to explain a little terminology.

Python’s primary organizational unit is known as a module. Module’s come in one of two flavors: code modules and package modules. A code module is any file that contains python executable code. A package module is a directory that contains other modules (either code modules or package modules). The most common type of code modules are *.py files while the most common type of package modules are directories containing an __init__.py file.

Python allows modules to be uniquely identified in two distinct ways: modulename and filename. In general, modules are identified by modulename in Python code (e.g., import <modulename>) and by filename on the command line (e.g., python <filename>). All python interpreters are able to convert modulenames to filenames by following the same few, well-defined rules. These rules hinge on the sys.path variable. By altering this variable one can change how Python resolves modulenames into filenames (for more on how this is done see PEP 302).

All modules (both code and package) can be executed (i.e., code associated with the module will be evaluated by the Python interpreter). Depending on the execution method (and module type) what code gets evaluated, and when, can change quite a bit. For example, if one executes a package module via python <filename> then <filename>/__init__.py will be evaluated followed by <filename>/__main__.py. On the other hand, if one executes that same package module via import <modulename> then only the package’s __init__.py will be executed.

Historical Development of -m

The -m flag was first introduced in Python 2.4.1. Initially its only purpose was to provide an alternative means of identifying the python module to execute from the command line. That is, if we knew both the <filename> and <modulename> for a module then the following two commands were equivalent: python <filename> <args> and python -m <modulename> <args>. One constraint with this iteration, according to PEP 338, was that -m only worked with top level modulenames (i.e., modules that could be found directly on sys.path without any intervening package modules).

With the completion of PEP 338 the -m feature was extended to support <modulename> representations beyond the top level. This meant names such as http.server were now fully supported. This extension also meant that each parent package in modulename was now evaluated (i.e., all parent package __init__.py files were evaluated) in addition to the module referenced by the modulename itself.

The final major feature enhancement for -m came with PEP 366. With this upgrade -m gained the ability to support not only absolute imports but also explicit relative imports when executing modules. This was achieved by changing -m so that it set the __package__ variable to the parent module of the given modulename (in addition to everything else it already did).

Use Cases

There are two notable use cases for the -m flag:

  1. To execute modules from the command line for which one may not know their filename. This use case takes advantage of the fact that the Python interpreter knows how to convert modulenames to filenames. This is particularly advantageous when one wants to run stdlib modules or 3rd-party module from the command line. For example, very few people know the filename for the http.server module but most people do know its modulename so we can execute it from the command line using python -m http.server.

  2. To execute a local package containing absolute or relative imports without needing to install it. This use case is detailed in PEP 338 and leverages the fact that the current working directory is added to sys.path rather than the module’s directory. This use case is very similar to using pip install -e . to install a package in develop/edit mode.

Shortcomings

With all the enhancements made to -m over the years it still has one major shortcoming — it can only execute modules written in Python (i.e., *.py). For example, if -m is used to execute a C compiled code module the following error will be produced, No code object available for <modulename> (see here for more details).

Detailed Comparisons

Effects of module execution via import statement (i.e., import <modulename>):

  • sys.path is not modified in any way
  • __name__ is set to the absolute form of <modulename>
  • __package__ is set to the immediate parent package in <modulename>
  • __init__.py is evaluated for all packages (including its own for package modules)
  • __main__.py is not evaluated for package modules; the code is evaluated for code modules

Effects of module execution via command line (i.e., python <filename>):

  • sys.path is modified to include the final directory in <filename>
  • __name__ is set to '__main__'
  • __package__ is set to None
  • __init__.py is not evaluated for any package (including its own for package modules)
  • __main__.py is evaluated for package modules; the code is evaluated for code modules.

Effects of module execution via command line with the -m flag (i.e., python -m <modulename>):

  • sys.path is modified to include the current directory
  • __name__ is set to '__main__'
  • __package__ is set to the immediate parent package in <modulename>
  • __init__.py is evaluated for all packages (including its own for package modules)
  • __main__.py is evaluated for package modules; the code is evaluated for code modules

Conclusion

The -m flag is, at its simplest, a means to execute python scripts from the command line by using modulenames rather than filenames. The real power of -m, however, is in its ability to combine the power of import statements (e.g., support for explicit relative imports and automatic package __init__ evaluation) with the convenience of the command line.


为什么在导入模块时Python运行我的模块,以及如何停止它?

问题:为什么在导入模块时Python运行我的模块,以及如何停止它?

我有一个正在构建的Python程序,可以通过以下两种方式之一运行:第一种是调用“ python main.py”,它以友好的方式提示用户输入,然后通过该程序运行用户输入。另一种方法是调用“ python batch.py -file- ”,它将遍历所有友好的输入集合,并通过该程序一次运行整个文件的输入值。

问题是,当我运行“ batch.py​​”时,它会从“ main.py”中导入一些变量/方法/等,并在运行此代码时:

import main

在程序的第一行,它立即错误,因为它试图运行“ main.py”中的代码。

如何阻止Python运行要导入的“主”模块中包含的代码?

I have a Python program I’m building that can be run in either of 2 ways: the first is to call “python main.py” which prompts the user for input in a friendly manner and then runs the user input through the program. The other way is to call “python batch.py -file-” which will pass over all the friendly input gathering and run an entire file’s worth of input through the program in a single go.

The problem is that when I run “batch.py” it imports some variables/methods/etc from “main.py”, and when it runs this code:

import main

at the first line of the program, it immediately errors because it tries to run the code in “main.py”.

How can I stop Python from running the code contained in the “main” module which I’m importing?


回答 0

因为这就是Python的工作方式,诸如classand之def类的关键字不是声明。相反,它们是执行的真实实时语句。如果未执行,则您的模块将为..空:-)

无论如何,惯用的方法是:

# stuff to run always here such as class/def
def main():
    pass

if __name__ == "__main__":
   # stuff only to run when not called via 'import' here
   main()

请参阅目的是什么if __name__ == "__main__"

但是,它确实需要对要import编辑的模块进行源代码控制。

快乐的编码。

Because this is just how Python works – keywords such as class and def are not declarations. Instead, they are real live statements which are executed. If they were not executed your module would be .. empty :-)

Anyway, the idiomatic approach is:

# stuff to run always here such as class/def
def main():
    pass

if __name__ == "__main__":
   # stuff only to run when not called via 'import' here
   main()

See What is if __name__ == "__main__" for?

It does require source control over the module being imported, however.

Happy coding.


回答 1

由于Python的工作方式,在导入模块时必须运行模块。

为了防止模块中的代码在导入时被执行,而只能在直接运行时执行,可以使用以下方法进行保护if

if __name__ == "__main__":
    # this won't be run when imported

您可能需要将此代码放入main()方法中,以便可以直接执行文件,或导入模块并调用main()。例如,假设它在file中foo.py

def main():
    print "Hello World"

if __name__ == "__main__":
    main()

该程序可以通过运行python foo.py或从另一个Python脚本运行:

import foo

...

foo.main()

Due to the way Python works, it is necessary for it to run your modules when it imports them.

To prevent code in the module from being executed when imported, but only when run directly, you can guard it with this if:

if __name__ == "__main__":
    # this won't be run when imported

You may want to put this code in a main() method, so that you can either execute the file directly, or import the module and call the main(). For example, assume this is in the file foo.py.

def main():
    print "Hello World"

if __name__ == "__main__":
    main()

This program can be run either by going python foo.py, or from another Python script:

import foo

...

foo.main()

回答 2

使用if __name__ == '__main__'惯用语- __name__是一个特殊变量,其值是'__main__'模块是否作为脚本运行时的值,模块名称(如果已导入)。所以你会做类似的事情

# imports
# class/function definitions
if __name__ == '__main__':
    # code here will only run when you invoke 'python main.py'

Use the if __name__ == '__main__' idiom — __name__ is a special variable whose value is '__main__' if the module is being run as a script, and the module name if it’s imported. So you’d do something like

# imports
# class/function definitions
if __name__ == '__main__':
    # code here will only run when you invoke 'python main.py'

回答 3

不幸的是,您没有。这是导入语法工作方式的一部分,并且重要的是,请记住这一点-记住def实际上是已执行某项操作,如果Python不执行导入,那么您将被困在没有函数的情况下。

但是,由于您可能可以访问该文件,因此您可以查看并查看导致该错误的原因。可能有可能修改您的环境以防止发生错误。

Unfortunately, you don’t. That is part of how the import syntax works and it is important that it does so — remember def is actually something executed, if Python did not execute the import, you’d be, well, stuck without functions.

Since you probably have access to the file, though, you might be able to look and see what causes the error. It might be possible to modify your environment to prevent the error from happening.


回答 4

将代码放入函数中,直到调用该函数,该代码才会运行。您应该在其中具有主要功能main.py。带有以下语句:

if __name__ == '__main__':
  main()

然后,如果调用python main.pymain()函数将运行。如果导入main.py,则不会。另外,main.py为清楚起见,您可能应该重命名为其他名称。

Put the code inside a function and it won’t run until you call the function. You should have a main function in your main.py. with the statement:

if __name__ == '__main__':
  main()

Then, if you call python main.py the main() function will run. If you import main.py, it will not. Also, you should probably rename main.py to something else for clarity’s sake.


回答 5

有一个Python增强建议PEP 299,旨在用替换if __name__ == '__main__':成语def __main__:,但遭到拒绝。了解使用时要记住的内容仍然是一本好书if __name__ = '__main__':

There was a Python enhancement proposal PEP 299 which aimed to replace if __name__ == '__main__': idiom with def __main__:, but it was rejected. It’s still a good read to know what to keep in mind when using if __name__ = '__main__':.


回答 6

您可以这样编写“ main.py”:

#!/usr/bin/env python

__all__=["somevar", "do_something"]

somevar=""

def do_something():
    pass #blahblah

if __name__=="__main__":
    do_something()

You may write your “main.py” like this:

#!/usr/bin/env python

__all__=["somevar", "do_something"]

somevar=""

def do_something():
    pass #blahblah

if __name__=="__main__":
    do_something()

回答 7

尽管import不运行代码就无法使用;您可以通过很快速的方式输入变量;通过使用numpy.savez,将变量作为numpy数组存储在.npz文件中。之后,您可以使用加载变量numpy.load

查看scipy文档中的完整描述

请注意,这仅适用于变量和变量数组,而不适用于方法等。

Although you cannot use import without running the code; there is quite a swift way in which you can input your variables; by using numpy.savez, which stores variables as numpy arrays in a .npz file. Afterwards you can load the variables using numpy.load.

See a full description in the scipy documentation

Please note this is only the case for variables and arrays of variable, and not for methods, etc.


回答 8

尝试仅从main.py导入所需的功能?所以,

from main import SomeFunction

可能是因为您在batch.py​​中命名的函数与main.py中的命名相同,并且在导入main.py时,程序将运行main.py函数而不是batch.py​​函数;执行以上操作可以解决该问题。我希望。

Try just importing the functions needed from main.py? So,

from main import SomeFunction

It could be that you’ve named a function in batch.py the same as one in main.py, and when you import main.py the program runs the main.py function instead of the batch.py function; doing the above should fix that. I hope.


生成具有给定(数字)分布的随机数

问题:生成具有给定(数字)分布的随机数

我有一个具有不同值的概率的文件,例如:

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2

我想使用此分布生成随机数。是否存在处理此问题的现有模块?自己编写代码是很简单的(构建累积密度函数,生成随机值[0,1]并选择相应的值),但这似乎是一个常见问题,可能有人为它创建了一个函数/模块它。

我需要这个,因为我想生成一个生日列表(不遵循标准random模块中的任何分布)。

I have a file with some probabilities for different values e.g.:

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2

I would like to generate random numbers using this distribution. Does an existing module that handles this exist? It’s fairly simple to code on your own (build the cumulative density function, generate a random value [0,1] and pick the corresponding value) but it seems like this should be a common problem and probably someone has created a function/module for it.

I need this because I want to generate a list of birthdays (which do not follow any distribution in the standard random module).


回答 0

scipy.stats.rv_discrete可能就是您想要的。您可以通过values参数提供概率。然后,您可以使用rvs()分发对象的方法来生成随机数。

正如Eugene Pakhomov在评论中指出的那样,您还可以将p关键字参数传递给numpy.random.choice(),例如

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

如果您使用的是Python 3.6或更高版本,则可以random.choices()在标准库中使用–请参见Mark Dickinson答案

scipy.stats.rv_discrete might be what you want. You can supply your probabilities via the values parameter. You can then use the rvs() method of the distribution object to generate random numbers.

As pointed out by Eugene Pakhomov in the comments, you can also pass a p keyword parameter to numpy.random.choice(), e.g.

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

If you are using Python 3.6 or above, you can use random.choices() from the standard library – see the answer by Mark Dickinson.


回答 1

从Python 3.6开始,Python的标准库中提供了一个解决方案random.choices

用法示例:让我们设置与OP中的问题相匹配的总体和权重:

>>> from random import choices
>>> population = [1, 2, 3, 4, 5, 6]
>>> weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]

现在choices(population, weights)生成一个样本:

>>> choices(population, weights)
4

可选的仅关键字参数k允许一个参数一次请求多个样本。这很有价值,因为random.choices在生成任何样本之前,每次调用时都要做一些准备工作。通过一次生成许多样本,我们只需要做一次准备工作。在这里,我们生成了一百万个样本,并collections.Counter用来检查我们得到的分布与我们赋予的权重大致匹配。

>>> million_samples = choices(population, weights, k=10**6)
>>> from collections import Counter
>>> Counter(million_samples)
Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636, 3: 50219, 2: 50025})

Since Python 3.6, there’s a solution for this in Python’s standard library, namely random.choices.

Example usage: let’s set up a population and weights matching those in the OP’s question:

>>> from random import choices
>>> population = [1, 2, 3, 4, 5, 6]
>>> weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]

Now choices(population, weights) generates a single sample:

>>> choices(population, weights)
4

The optional keyword-only argument k allows one to request more than one sample at once. This is valuable because there’s some preparatory work that random.choices has to do every time it’s called, prior to generating any samples; by generating many samples at once, we only have to do that preparatory work once. Here we generate a million samples, and use collections.Counter to check that the distribution we get roughly matches the weights we gave.

>>> million_samples = choices(population, weights, k=10**6)
>>> from collections import Counter
>>> Counter(million_samples)
Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636, 3: 50219, 2: 50025})

回答 2

使用CDF生成列表的一个优点是可以使用二进制搜索。当您需要O(n)的时间和空间进行预处理时,您可以在O(k log n)中获得k个数字。由于普通的Python列表效率低下,因此可以使用array模块。

如果您坚持使用恒定的空间,则可以执行以下操作;O(n)时间,O(1)空间。

def random_distr(l):
    r = random.uniform(0, 1)
    s = 0
    for item, prob in l:
        s += prob
        if s >= r:
            return item
    return item  # Might occur because of floating point inaccuracies

An advantage to generating the list using CDF is that you can use binary search. While you need O(n) time and space for preprocessing, you can get k numbers in O(k log n). Since normal Python lists are inefficient, you can use array module.

If you insist on constant space, you can do the following; O(n) time, O(1) space.

def random_distr(l):
    r = random.uniform(0, 1)
    s = 0
    for item, prob in l:
        s += prob
        if s >= r:
            return item
    return item  # Might occur because of floating point inaccuracies

回答 3

也许有点晚了。但是您可以使用numpy.random.choice()传递p参数:

val = numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

Maybe it is kind of late. But you can use numpy.random.choice(), passing the p parameter:

val = numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

回答 4

(好吧,我知道您正在要求收缩包装,但是也许这些自制的解决方案还不够简洁,无法满足您的喜好。:-)

pdf = [(1, 0.1), (2, 0.05), (3, 0.05), (4, 0.2), (5, 0.4), (6, 0.2)]
cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]
R = max(i for r in [random.random()] for i,c in cdf if c <= r)

我通过确认此表达式的输出来伪确认此方法有效:

sorted(max(i for r in [random.random()] for i,c in cdf if c <= r)
       for _ in range(1000))

(OK, I know you are asking for shrink-wrap, but maybe those home-grown solutions just weren’t succinct enough for your liking. :-)

pdf = [(1, 0.1), (2, 0.05), (3, 0.05), (4, 0.2), (5, 0.4), (6, 0.2)]
cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]
R = max(i for r in [random.random()] for i,c in cdf if c <= r)

I pseudo-confirmed that this works by eyeballing the output of this expression:

sorted(max(i for r in [random.random()] for i,c in cdf if c <= r)
       for _ in range(1000))

回答 5

我写了一个从自定义连续分布中抽取随机样本的解决方案。

我需要一个与您的用例类似的用例(即生成具有给定概率分布的随机日期)。

您只需要功能random_custDist和功能samples=random_custDist(x0,x1,custDist=custDist,size=1000)。剩下的就是装饰^^。

import numpy as np

#funtion
def random_custDist(x0,x1,custDist,size=None, nControl=10**6):
    #genearte a list of size random samples, obeying the distribution custDist
    #suggests random samples between x0 and x1 and accepts the suggestion with probability custDist(x)
    #custDist noes not need to be normalized. Add this condition to increase performance. 
    #Best performance for max_{x in [x0,x1]} custDist(x) = 1
    samples=[]
    nLoop=0
    while len(samples)<size and nLoop<nControl:
        x=np.random.uniform(low=x0,high=x1)
        prop=custDist(x)
        assert prop>=0 and prop<=1
        if np.random.uniform(low=0,high=1) <=prop:
            samples += [x]
        nLoop+=1
    return samples

#call
x0=2007
x1=2019
def custDist(x):
    if x<2010:
        return .3
    else:
        return (np.exp(x-2008)-1)/(np.exp(2019-2007)-1)
samples=random_custDist(x0,x1,custDist=custDist,size=1000)
print(samples)

#plot
import matplotlib.pyplot as plt
#hist
bins=np.linspace(x0,x1,int(x1-x0+1))
hist=np.histogram(samples, bins )[0]
hist=hist/np.sum(hist)
plt.bar( (bins[:-1]+bins[1:])/2, hist, width=.96, label='sample distribution')
#dist
grid=np.linspace(x0,x1,100)
discCustDist=np.array([custDist(x) for x in grid]) #distrete version
discCustDist*=1/(grid[1]-grid[0])/np.sum(discCustDist)
plt.plot(grid,discCustDist,label='custom distribustion (custDist)', color='C1', linewidth=4)
#decoration
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()

该解决方案的性能肯定可以提高,但是我更喜欢可读性。

I wrote a solution for drawing random samples from a custom continuous distribution.

I needed this for a similar use-case to yours (i.e. generating random dates with a given probability distribution).

You just need the funtion random_custDist and the line samples=random_custDist(x0,x1,custDist=custDist,size=1000). The rest is decoration ^^.

import numpy as np

#funtion
def random_custDist(x0,x1,custDist,size=None, nControl=10**6):
    #genearte a list of size random samples, obeying the distribution custDist
    #suggests random samples between x0 and x1 and accepts the suggestion with probability custDist(x)
    #custDist noes not need to be normalized. Add this condition to increase performance. 
    #Best performance for max_{x in [x0,x1]} custDist(x) = 1
    samples=[]
    nLoop=0
    while len(samples)<size and nLoop<nControl:
        x=np.random.uniform(low=x0,high=x1)
        prop=custDist(x)
        assert prop>=0 and prop<=1
        if np.random.uniform(low=0,high=1) <=prop:
            samples += [x]
        nLoop+=1
    return samples

#call
x0=2007
x1=2019
def custDist(x):
    if x<2010:
        return .3
    else:
        return (np.exp(x-2008)-1)/(np.exp(2019-2007)-1)
samples=random_custDist(x0,x1,custDist=custDist,size=1000)
print(samples)

#plot
import matplotlib.pyplot as plt
#hist
bins=np.linspace(x0,x1,int(x1-x0+1))
hist=np.histogram(samples, bins )[0]
hist=hist/np.sum(hist)
plt.bar( (bins[:-1]+bins[1:])/2, hist, width=.96, label='sample distribution')
#dist
grid=np.linspace(x0,x1,100)
discCustDist=np.array([custDist(x) for x in grid]) #distrete version
discCustDist*=1/(grid[1]-grid[0])/np.sum(discCustDist)
plt.plot(grid,discCustDist,label='custom distribustion (custDist)', color='C1', linewidth=4)
#decoration
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()

The performance of this solution is improvable for sure, but I prefer readability.


回答 6

根据以下内容列出项目weights

items = [1, 2, 3, 4, 5, 6]
probabilities= [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]
# if the list of probs is normalized (sum(probs) == 1), omit this part
prob = sum(probabilities) # find sum of probs, to normalize them
c = (1.0)/prob # a multiplier to make a list of normalized probs
probabilities = map(lambda x: c*x, probabilities)
print probabilities

ml = max(probabilities, key=lambda x: len(str(x)) - str(x).find('.'))
ml = len(str(ml)) - str(ml).find('.') -1
amounts = [ int(x*(10**ml)) for x in probabilities]
itemsList = list()
for i in range(0, len(items)): # iterate through original items
  itemsList += items[i:i+1]*amounts[i]

# choose from itemsList randomly
print itemsList

一种优化可能是通过最大公约数对数量进行归一化,以使目标列表更小。

另外,可能很有趣。

Make a list of items, based on their weights:

items = [1, 2, 3, 4, 5, 6]
probabilities= [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]
# if the list of probs is normalized (sum(probs) == 1), omit this part
prob = sum(probabilities) # find sum of probs, to normalize them
c = (1.0)/prob # a multiplier to make a list of normalized probs
probabilities = map(lambda x: c*x, probabilities)
print probabilities

ml = max(probabilities, key=lambda x: len(str(x)) - str(x).find('.'))
ml = len(str(ml)) - str(ml).find('.') -1
amounts = [ int(x*(10**ml)) for x in probabilities]
itemsList = list()
for i in range(0, len(items)): # iterate through original items
  itemsList += items[i:i+1]*amounts[i]

# choose from itemsList randomly
print itemsList

An optimization may be to normalize amounts by the greatest common divisor, to make the target list smaller.

Also, this might be interesting.


回答 7

另一个答案,可能更快:)

distribution = [(1, 0.2), (2, 0.3), (3, 0.5)]  
# init distribution  
dlist = []  
sumchance = 0  
for value, chance in distribution:  
    sumchance += chance  
    dlist.append((value, sumchance))  
assert sumchance == 1.0 # not good assert because of float equality  

# get random value  
r = random.random()  
# for small distributions use lineair search  
if len(distribution) < 64: # don't know exact speed limit  
    for value, sumchance in dlist:  
        if r < sumchance:  
            return value  
else:  
    # else (not implemented) binary search algorithm  

Another answer, probably faster :)

distribution = [(1, 0.2), (2, 0.3), (3, 0.5)]  
# init distribution  
dlist = []  
sumchance = 0  
for value, chance in distribution:  
    sumchance += chance  
    dlist.append((value, sumchance))  
assert sumchance == 1.0 # not good assert because of float equality  

# get random value  
r = random.random()  
# for small distributions use lineair search  
if len(distribution) < 64: # don't know exact speed limit  
    for value, sumchance in dlist:  
        if r < sumchance:  
            return value  
else:  
    # else (not implemented) binary search algorithm  

回答 8

from __future__ import division
import random
from collections import Counter


def num_gen(num_probs):
    # calculate minimum probability to normalize
    min_prob = min(prob for num, prob in num_probs)
    lst = []
    for num, prob in num_probs:
        # keep appending num to lst, proportional to its probability in the distribution
        for _ in range(int(prob/min_prob)):
            lst.append(num)
    # all elems in lst occur proportional to their distribution probablities
    while True:
        # pick a random index from lst
        ind = random.randint(0, len(lst)-1)
        yield lst[ind]

验证:

gen = num_gen([(1, 0.1),
               (2, 0.05),
               (3, 0.05),
               (4, 0.2),
               (5, 0.4),
               (6, 0.2)])
lst = []
times = 10000
for _ in range(times):
    lst.append(next(gen))
# Verify the created distribution:
for item, count in Counter(lst).iteritems():
    print '%d has %f probability' % (item, count/times)

1 has 0.099737 probability
2 has 0.050022 probability
3 has 0.049996 probability 
4 has 0.200154 probability
5 has 0.399791 probability
6 has 0.200300 probability
from __future__ import division
import random
from collections import Counter


def num_gen(num_probs):
    # calculate minimum probability to normalize
    min_prob = min(prob for num, prob in num_probs)
    lst = []
    for num, prob in num_probs:
        # keep appending num to lst, proportional to its probability in the distribution
        for _ in range(int(prob/min_prob)):
            lst.append(num)
    # all elems in lst occur proportional to their distribution probablities
    while True:
        # pick a random index from lst
        ind = random.randint(0, len(lst)-1)
        yield lst[ind]

Verification:

gen = num_gen([(1, 0.1),
               (2, 0.05),
               (3, 0.05),
               (4, 0.2),
               (5, 0.4),
               (6, 0.2)])
lst = []
times = 10000
for _ in range(times):
    lst.append(next(gen))
# Verify the created distribution:
for item, count in Counter(lst).iteritems():
    print '%d has %f probability' % (item, count/times)

1 has 0.099737 probability
2 has 0.050022 probability
3 has 0.049996 probability 
4 has 0.200154 probability
5 has 0.399791 probability
6 has 0.200300 probability

回答 9

根据其他解决方案,您可以生成累积分布(任意形式为整数或浮点数),然后可以使用二等分来使其快速

这是一个简单的示例(我在这里使用了整数)

l=[(20, 'foo'), (60, 'banana'), (10, 'monkey'), (10, 'monkey2')]
def get_cdf(l):
    ret=[]
    c=0
    for i in l: c+=i[0]; ret.append((c, i[1]))
    return ret

def get_random_item(cdf):
    return cdf[bisect.bisect_left(cdf, (random.randint(0, cdf[-1][0]),))][1]

cdf=get_cdf(l)
for i in range(100): print get_random_item(cdf),

get_cdf函数会将其从20、60、10、10转换为20、20 + 60、20 + 60 + 10、20 + 60 + 10 + 10

现在我们使用选取最大为20 + 60 + 10 + 10的随机数,random.randint然后使用bisect快速获取实际值

based on other solutions, you generate accumulative distribution (as integer or float whatever you like), then you can use bisect to make it fast

this is a simple example (I used integers here)

l=[(20, 'foo'), (60, 'banana'), (10, 'monkey'), (10, 'monkey2')]
def get_cdf(l):
    ret=[]
    c=0
    for i in l: c+=i[0]; ret.append((c, i[1]))
    return ret

def get_random_item(cdf):
    return cdf[bisect.bisect_left(cdf, (random.randint(0, cdf[-1][0]),))][1]

cdf=get_cdf(l)
for i in range(100): print get_random_item(cdf),

the get_cdf function would convert it from 20, 60, 10, 10 into 20, 20+60, 20+60+10, 20+60+10+10

now we pick a random number up to 20+60+10+10 using random.randint then we use bisect to get the actual value in a fast way


回答 10

您可能想看看NumPy 随机抽样分布

you might want to have a look at NumPy Random sampling distributions


回答 11

这些答案都不是特别清楚或简单的。

这是保证可以正常工作的一种清晰,简单的方法。

accumulate_normalize_probabilities采用字典p将符号映射到概率频率。它输出要选择的元组的可用列表。

def accumulate_normalize_values(p):
        pi = p.items() if isinstance(p,dict) else p
        accum_pi = []
        accum = 0
        for i in pi:
                accum_pi.append((i[0],i[1]+accum))
                accum += i[1]
        if accum == 0:
                raise Exception( "You are about to explode the universe. Continue ? Y/N " )
        normed_a = []
        for a in accum_pi:
                normed_a.append((a[0],a[1]*1.0/accum))
        return normed_a

Yield:

>>> accumulate_normalize_values( { 'a': 100, 'b' : 300, 'c' : 400, 'd' : 200  } )
[('a', 0.1), ('c', 0.5), ('b', 0.8), ('d', 1.0)]

为什么运作

所述积累步骤变成每个符号到(在第一符号的情况下或0)本身和先前符号概率或频率之间的间隔。通过简单地逐步遍历列表,直到间隔0.0-> 1.0中的随机数(之前已准备好)小于或等于当前符号的间隔端点,可以使用这些间隔进行选择(从而对提供的分布进行采样)。

规范化释放我们从需求,以确保一切资金以一定的价值。归一化后,概率的“向量”总计为1.0。

下面用于从分布中选择并生成任意长样本的其余代码

def select(symbol_intervals,random):
        print symbol_intervals,random
        i = 0
        while random > symbol_intervals[i][1]:
                i += 1
                if i >= len(symbol_intervals):
                        raise Exception( "What did you DO to that poor list?" )
        return symbol_intervals[i][0]


def gen_random(alphabet,length,probabilities=None):
        from random import random
        from itertools import repeat
        if probabilities is None:
                probabilities = dict(zip(alphabet,repeat(1.0)))
        elif len(probabilities) > 0 and isinstance(probabilities[0],(int,long,float)):
                probabilities = dict(zip(alphabet,probabilities)) #ordered
        usable_probabilities = accumulate_normalize_values(probabilities)
        gen = []
        while len(gen) < length:
                gen.append(select(usable_probabilities,random()))
        return gen

用法:

>>> gen_random (['a','b','c','d'],10,[100,300,400,200])
['d', 'b', 'b', 'a', 'c', 'c', 'b', 'c', 'c', 'c']   #<--- some of the time

None of these answers is particularly clear or simple.

Here is a clear, simple method that is guaranteed to work.

accumulate_normalize_probabilities takes a dictionary p that maps symbols to probabilities OR frequencies. It outputs usable list of tuples from which to do selection.

def accumulate_normalize_values(p):
        pi = p.items() if isinstance(p,dict) else p
        accum_pi = []
        accum = 0
        for i in pi:
                accum_pi.append((i[0],i[1]+accum))
                accum += i[1]
        if accum == 0:
                raise Exception( "You are about to explode the universe. Continue ? Y/N " )
        normed_a = []
        for a in accum_pi:
                normed_a.append((a[0],a[1]*1.0/accum))
        return normed_a

Yields:

>>> accumulate_normalize_values( { 'a': 100, 'b' : 300, 'c' : 400, 'd' : 200  } )
[('a', 0.1), ('c', 0.5), ('b', 0.8), ('d', 1.0)]

Why it works

The accumulation step turns each symbol into an interval between itself and the previous symbols probability or frequency (or 0 in the case of the first symbol). These intervals can be used to select from (and thus sample the provided distribution) by simply stepping through the list until the random number in interval 0.0 -> 1.0 (prepared earlier) is less or equal to the current symbol’s interval end-point.

The normalization releases us from the need to make sure everything sums to some value. After normalization the “vector” of probabilities sums to 1.0.

The rest of the code for selection and generating a arbitrarily long sample from the distribution is below :

def select(symbol_intervals,random):
        print symbol_intervals,random
        i = 0
        while random > symbol_intervals[i][1]:
                i += 1
                if i >= len(symbol_intervals):
                        raise Exception( "What did you DO to that poor list?" )
        return symbol_intervals[i][0]


def gen_random(alphabet,length,probabilities=None):
        from random import random
        from itertools import repeat
        if probabilities is None:
                probabilities = dict(zip(alphabet,repeat(1.0)))
        elif len(probabilities) > 0 and isinstance(probabilities[0],(int,long,float)):
                probabilities = dict(zip(alphabet,probabilities)) #ordered
        usable_probabilities = accumulate_normalize_values(probabilities)
        gen = []
        while len(gen) < length:
                gen.append(select(usable_probabilities,random()))
        return gen

Usage :

>>> gen_random (['a','b','c','d'],10,[100,300,400,200])
['d', 'b', 'b', 'a', 'c', 'c', 'b', 'c', 'c', 'c']   #<--- some of the time

回答 12

这是一种更有效的方法

只需使用您的“权重”数组(假定索引为对应项)和否调用以下函数。需要的样本数。可以轻松修改此功能以处理有序对。

使用各自的概率返回采样/挑选(替换)的索引(或项目):

def resample(weights, n):
    beta = 0

    # Caveat: Assign max weight to max*2 for best results
    max_w = max(weights)*2

    # Pick an item uniformly at random, to start with
    current_item = random.randint(0,n-1)
    result = []

    for i in range(n):
        beta += random.uniform(0,max_w)

        while weights[current_item] < beta:
            beta -= weights[current_item]
            current_item = (current_item + 1) % n   # cyclic
        else:
            result.append(current_item)
    return result

关于while循环中使用的概念的简短说明。我们从累积beta减少当前项目的权重,该累积值是随机统一构造的累积值,并增加当前索引以找到其权重与beta值匹配的项目。

Here is a more effective way of doing this:

Just call the following function with your ‘weights’ array (assuming the indices as the corresponding items) and the no. of samples needed. This function can be easily modified to handle ordered pair.

Returns indexes (or items) sampled/picked (with replacement) using their respective probabilities:

def resample(weights, n):
    beta = 0

    # Caveat: Assign max weight to max*2 for best results
    max_w = max(weights)*2

    # Pick an item uniformly at random, to start with
    current_item = random.randint(0,n-1)
    result = []

    for i in range(n):
        beta += random.uniform(0,max_w)

        while weights[current_item] < beta:
            beta -= weights[current_item]
            current_item = (current_item + 1) % n   # cyclic
        else:
            result.append(current_item)
    return result

A short note on the concept used in the while loop. We reduce the current item’s weight from cumulative beta, which is a cumulative value constructed uniformly at random, and increment current index in order to find the item, the weight of which matches the value of beta.


我可以使用__init__.py定义全局变量吗?

问题:我可以使用__init__.py定义全局变量吗?

我想定义一个常量,该常量应在包的所有子模块中可用。我以为最好的地方__init__.py在根包的文件中。但是我不知道该怎么做。假设我有几个子包,每个子包都有几个模块。如何从这些模块访问该变量?

当然,如果这是完全错误的,并且有更好的选择,我想知道。

I want to define a constant that should be available in all of the submodules of a package. I’ve thought that the best place would be in in the __init__.py file of the root package. But I don’t know how to do this. Suppose I have a few subpackages and each with several modules. How can I access that variable from these modules?

Of course, if this is totally wrong, and there is a better alternative, I’d like to know it.


回答 0

您应该能够将它们放入__init__.py。这一直都在做。

mypackage/__init__.py

MY_CONSTANT = 42

mypackage/mymodule.py

from mypackage import MY_CONSTANT
print "my constant is", MY_CONSTANT

然后,导入mymodule:

>>> from mypackage import mymodule
my constant is 42

不过,如果您确实有常量,将它们放在单独的模块(constants.py,config.py,…)中,然后将其放入包命名空间中是合理的(可能是最佳做法),然后导入他们。

mypackage/__init__.py

from mypackage.constants import *

尽管如此,这并不会自动在包模块的命名空间中包含常量。包中的每个模块仍然必须从mypackage或从中显式导入常量mypackage.constants

You should be able to put them in __init__.py. This is done all the time.

mypackage/__init__.py:

MY_CONSTANT = 42

mypackage/mymodule.py:

from mypackage import MY_CONSTANT
print "my constant is", MY_CONSTANT

Then, import mymodule:

>>> from mypackage import mymodule
my constant is 42

Still, if you do have constants, it would be reasonable (best practices, probably) to put them in a separate module (constants.py, config.py, …) and then if you want them in the package namespace, import them.

mypackage/__init__.py:

from mypackage.constants import *

Still, this doesn’t automatically include the constants in the namespaces of the package modules. Each of the modules in the package will still have to import constants explicitly either from mypackage or from mypackage.constants.


回答 1

你不能这样做。您必须将常量明确地导入每个模块的命名空间中。实现此目的的最佳方法是在“ config”模块中定义常量,并将其导入所需的任何位置:

# mypackage/config.py
MY_CONST = 17

# mypackage/main.py
from mypackage.config import *

You cannot do that. You will have to explicitely import your constants into each individual module’s namespace. The best way to achieve this is to define your constants in a “config” module and import it everywhere you require it:

# mypackage/config.py
MY_CONST = 17

# mypackage/main.py
from mypackage.config import *

回答 2

您可以在任何地方定义全局变量,但这是一个非常糟糕的主意。导入__builtin__模块并修改或向该模块添加属性,突然间您有了新的内置常量或函数。实际上,当我的应用程序安装gettext时,我在所有模块中都获得了_()函数,而无需导入任何内容。因此这是可能的,但当然仅适用于应用程序类型的项目,而不适用于可重用的包或模块。

而且我想没人会推荐这种做法。命名空间有什么问题?说应用程序版本模块,这样我可以有像“全局”变量version.VERSIONversion.PACKAGE_NAME等等。

You can define global variables from anywhere, but it is a really bad idea. import the __builtin__ module and modify or add attributes to this modules, and suddenly you have new builtin constants or functions. In fact, when my application installs gettext, I get the _() function in all my modules, without importing anything. So this is possible, but of course only for Application-type projects, not for reusable packages or modules.

And I guess no one would recommend this practice anyway. What’s wrong with a namespace? Said application has the version module, so that I have “global” variables available like version.VERSION, version.PACKAGE_NAME etc.


回答 3

只是想补充一点,可以使用config.ini文件使用常量,并使用configparser库在脚本中对其进行解析。这样,您可以在多种情况下使用常量。例如,如果您有两个单独的url请求的参数常量,只需将它们标记为:

mymodule/config.ini
[request0]
conn = 'admin@localhost'
pass = 'admin'
...

[request1]
conn = 'barney@localhost'
pass = 'dinosaur'
...

我发现Python网站上的文档非常有帮助。我不确定Python 2和3之间是否有任何区别,因此这是两者的链接:

对于Python 3:https//docs.python.org/3/library/configparser.html#module-configparser

对于Python 2:https//docs.python.org/2/library/configparser.html#module-configparser

Just wanted to add that constants can be employed using a config.ini file and parsed in the script using the configparser library. This way you could have constants for multiple circumstances. For instance if you had parameter constants for two separate url requests just label them like so:

mymodule/config.ini
[request0]
conn = 'admin@localhost'
pass = 'admin'
...

[request1]
conn = 'barney@localhost'
pass = 'dinosaur'
...

I found the documentation on the Python website very helpful. I am not sure if there are any differences between Python 2 and 3 so here are the links to both:

For Python 3: https://docs.python.org/3/library/configparser.html#module-configparser

For Python 2: https://docs.python.org/2/library/configparser.html#module-configparser


__getattr__在模块上

问题:__getattr__在模块上

如何实现等效的 __getattr__在类,模块上于a?

当调用模块的静态定义的属性中不存在的函数时,我希望在该模块中创建一个类的实例,并使用与该模块上的属性查找失败相同的名称调用该方法。

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
    return getattr(A(), name)

if __name__ == "__main__":
    # i hope here to have my __getattr__ function above invoked, since
    # salutation does not exist in the current namespace
    salutation("world")

这使:

matt@stanley:~/Desktop$ python getattrmod.py 
Traceback (most recent call last):
  File "getattrmod.py", line 9, in <module>
    salutation("world")
NameError: name 'salutation' is not defined

How can implement the equivalent of a __getattr__ on a class, on a module?

Example

When calling a function that does not exist in a module’s statically defined attributes, I wish to create an instance of a class in that module, and invoke the method on it with the same name as failed in the attribute lookup on the module.

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
    return getattr(A(), name)

if __name__ == "__main__":
    # i hope here to have my __getattr__ function above invoked, since
    # salutation does not exist in the current namespace
    salutation("world")

Which gives:

matt@stanley:~/Desktop$ python getattrmod.py 
Traceback (most recent call last):
  File "getattrmod.py", line 9, in <module>
    salutation("world")
NameError: name 'salutation' is not defined

回答 0

不久前,Guido宣布对新型类的所有特殊方法查找都绕过__getattr__and__getattribute__。Dunder方法曾经工作的模块-你可以,例如,使用一个模块作为一个上下文管理器简单地通过定义__enter____exit__,这些技巧之前爆发

最近,一些历史功能已经卷土重来,其中的一个模块已被卷土重来,__getattr__因此,sys.modules不再需要现有的hack(在导入时将一个模块替换为一个类)。

在Python 3.7+中,您仅使用一种显而易见的方法。要自定义模块上的属性访问,请__getattr__在模块级别定义一个函数,该函数应接受一个参数(属性名称),然后返回计算值或引发一个AttributeError

# my_module.py

def __getattr__(name: str) -> Any:
    ...

这也将允许钩子插入“ from”导入,即,您可以为语句(例如)返回动态生成的对象 from my_module import whatever

与此相关的是,您还可以与模块getattr一起__dir__在模块级别定义一个函数以响应dir(my_module)。有关详细信息,请参见PEP 562

A while ago, Guido declared that all special method lookups on new-style classes bypass __getattr__ and __getattribute__. Dunder methods had previously worked on modules – you could, for example, use a module as a context manager simply by defining __enter__ and __exit__, before those tricks broke.

Recently some historical features have made a comeback, the module __getattr__ among them, and so the existing hack (a module replacing itself with a class in sys.modules at import time) should be no longer necessary.

In Python 3.7+, you just use the one obvious way. To customize attribute access on a module, define a __getattr__ function at the module level which should accept one argument (name of attribute), and return the computed value or raise an AttributeError:

# my_module.py

def __getattr__(name: str) -> Any:
    ...

This will also allow hooks into “from” imports, i.e. you can return dynamically generated objects for statements such as from my_module import whatever.

On a related note, along with the module getattr you may also define a __dir__ function at module level to respond to dir(my_module). See PEP 562 for details.


回答 1

您在这里遇到两个基本问题:

  1. __xxx__ 方法只在类上查找
  2. TypeError: can't set attributes of built-in/extension type 'module'

(1)表示任何解决方案还必须跟踪正在检查的模块,否则每个模块将具有实例替换行为;(2)表示(1)甚至是不可能的……至少不是直接的。

幸运的是,sys.modules对那里发生的事情并不挑剔,因此可以使用包装器,但是只能用于模块访问(即import somemodule; somemodule.salutation('world'),对于相同模块的访问,您几乎必须从替换类中提取方法并将其添加到globals()eiher中。类上的自定义方法(我喜欢使用.export())或具有泛型函数(例如已经列出的答案)要记住的一件事:如果包装器每次都创建一个新实例,而全局解决方案不是,最终,您的行为会有所不同。哦,您不能同时使用两者-一种是另一种。


更新资料

Guido van Rossum出发:

实际上,偶尔会使用并推荐一种hack:一个模块可以用所需的功能定义一个类,然后最后,用该类的实例(如果需要,可以用该类)替换sys.modules中的自身。 ,但通常用处不大)。例如:

# module foo.py

import sys

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>

sys.modules[__name__] = Foo()

之所以可行,是因为导入机制正在积极地启用此hack,并且在加载的最后一步是将实际模块从sys.modules中拉出。(这绝非偶然。黑客是在很久以前就提出的,我们认为我们很喜欢在进口机器中提供支持。)

因此,完成所需操作的既定方法是在模块中创建一个类,并且作为模块的最后一步,sys.modules[__name__]用您的类的实例替换-现在您可以根据需要使用__getattr__/ __setattr__/ __getattribute__进行操作。


注意1:如果您使用此功能,则在进行sys.modules分配时,模块中的所有其他内容(例如全局变量,其他函数等)都会丢失-因此请确保所需的所有内容都在替换类之内。

注意2:要支持from module import *您必须__all__在类中进行定义;例如:

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>
    __all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})

根据您的Python版本,可能会省略其他名称__all__set()如果不需要Python 2兼容性,可以省略。

There are two basic problems you are running into here:

  1. __xxx__ methods are only looked up on the class
  2. TypeError: can't set attributes of built-in/extension type 'module'

(1) means any solution would have to also keep track of which module was being examined, otherwise every module would then have the instance-substitution behavior; and (2) means that (1) isn’t even possible… at least not directly.

Fortunately, sys.modules is not picky about what goes there so a wrapper will work, but only for module access (i.e. import somemodule; somemodule.salutation('world'); for same-module access you pretty much have to yank the methods from the substitution class and add them to globals() eiher with a custom method on the class (I like using .export()) or with a generic function (such as those already listed as answers). One thing to keep in mind: if the wrapper is creating a new instance each time, and the globals solution is not, you end up with subtly different behavior. Oh, and you don’t get to use both at the same time — it’s one or the other.


Update

From Guido van Rossum:

There is actually a hack that is occasionally used and recommended: a module can define a class with the desired functionality, and then at the end, replace itself in sys.modules with an instance of that class (or with the class, if you insist, but that’s generally less useful). E.g.:

# module foo.py

import sys

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>

sys.modules[__name__] = Foo()

This works because the import machinery is actively enabling this hack, and as its final step pulls the actual module out of sys.modules, after loading it. (This is no accident. The hack was proposed long ago and we decided we liked enough to support it in the import machinery.)

So the established way to accomplish what you want is to create a single class in your module, and as the last act of the module replace sys.modules[__name__] with an instance of your class — and now you can play with __getattr__/__setattr__/__getattribute__ as needed.


Note 1: If you use this functionality then anything else in the module, such as globals, other functions, etc., will be lost when the sys.modules assignment is made — so make sure everything needed is inside the replacement class.

Note 2: To support from module import * you must have __all__ defined in the class; for example:

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>
    __all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})

Depending on your Python version, there may be other names to omit from __all__. The set() can be omitted if Python 2 compatibility is not needed.


回答 2

这是一个技巧,但是您可以使用一个类包装模块:

class Wrapper(object):
  def __init__(self, wrapped):
    self.wrapped = wrapped
  def __getattr__(self, name):
    # Perform custom logic here
    try:
      return getattr(self.wrapped, name)
    except AttributeError:
      return 'default' # Some sensible default

sys.modules[__name__] = Wrapper(sys.modules[__name__])

This is a hack, but you can wrap the module with a class:

class Wrapper(object):
  def __init__(self, wrapped):
    self.wrapped = wrapped
  def __getattr__(self, name):
    # Perform custom logic here
    try:
      return getattr(self.wrapped, name)
    except AttributeError:
      return 'default' # Some sensible default

sys.modules[__name__] = Wrapper(sys.modules[__name__])

回答 3

我们通常不那样做。

我们要做的就是这个。

class A(object):
....

# The implicit global instance
a= A()

def salutation( *arg, **kw ):
    a.salutation( *arg, **kw )

为什么?使隐式全局实例可见。

例如,查看random模块,该模块创建一个隐式全局实例,以稍微简化您需要“简单”随机数生成器的用例。

We don’t usually do it that way.

What we do is this.

class A(object):
....

# The implicit global instance
a= A()

def salutation( *arg, **kw ):
    a.salutation( *arg, **kw )

Why? So that the implicit global instance is visible.

For examples, look at the random module, which creates an implicit global instance to slightly simplify the use cases where you want a “simple” random number generator.


回答 4

与@HåvardS提出的类似,在我需要在模块上实现一些魔术的情况下(例如__getattr__),我将定义一个继承types.ModuleType并放入其中的新类sys.modules(可能替换自定义模块ModuleType定义了定义)。

请参阅Werkzeug的主__init__.py文件,以实现此功能的强大功能。

Similar to what @Håvard S proposed, in a case where I needed to implement some magic on a module (like __getattr__), I would define a new class that inherits from types.ModuleType and put that in sys.modules (probably replacing the module where my custom ModuleType was defined).

See the main __init__.py file of Werkzeug for a fairly robust implementation of this.


回答 5

这有点黑,但是…

import types

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

    def farewell(self, greeting, accusative):
         print greeting, accusative

def AddGlobalAttribute(classname, methodname):
    print "Adding " + classname + "." + methodname + "()"
    def genericFunction(*args):
        return globals()[classname]().__getattribute__(methodname)(*args)
    globals()[methodname] = genericFunction

# set up the global namespace

x = 0   # X and Y are here to add them implicitly to globals, so
y = 0   # globals does not change as we iterate over it.

toAdd = []

def isCallableMethod(classname, methodname):
    someclass = globals()[classname]()
    something = someclass.__getattribute__(methodname)
    return callable(something)


for x in globals():
    print "Looking at", x
    if isinstance(globals()[x], (types.ClassType, type)):
        print "Found Class:", x
        for y in dir(globals()[x]):
            if y.find("__") == -1: # hack to ignore default methods
                if isCallableMethod(x,y):
                    if y not in globals(): # don't override existing global names
                        toAdd.append((x,y))


for x in toAdd:
    AddGlobalAttribute(*x)


if __name__ == "__main__":
    salutation("world")
    farewell("goodbye", "world")

通过遍历全局命名空间中的所有对象来工作。如果该项目是一个类,则在类属性上进行迭代。如果该属性是可调用的,则将其作为函数添加到全局命名空间中。

它忽略所有包含“ __”的属性。

我不会在生产代码中使用它,但是它应该可以帮助您入门。

This is hackish, but…

import types

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

    def farewell(self, greeting, accusative):
         print greeting, accusative

def AddGlobalAttribute(classname, methodname):
    print "Adding " + classname + "." + methodname + "()"
    def genericFunction(*args):
        return globals()[classname]().__getattribute__(methodname)(*args)
    globals()[methodname] = genericFunction

# set up the global namespace

x = 0   # X and Y are here to add them implicitly to globals, so
y = 0   # globals does not change as we iterate over it.

toAdd = []

def isCallableMethod(classname, methodname):
    someclass = globals()[classname]()
    something = someclass.__getattribute__(methodname)
    return callable(something)


for x in globals():
    print "Looking at", x
    if isinstance(globals()[x], (types.ClassType, type)):
        print "Found Class:", x
        for y in dir(globals()[x]):
            if y.find("__") == -1: # hack to ignore default methods
                if isCallableMethod(x,y):
                    if y not in globals(): # don't override existing global names
                        toAdd.append((x,y))


for x in toAdd:
    AddGlobalAttribute(*x)


if __name__ == "__main__":
    salutation("world")
    farewell("goodbye", "world")

This works by iterating over the all the objects in the global namespace. If the item is a class, it iterates over the class attributes. If the attribute is callable it adds it to the global namespace as a function.

It ignore all attributes which contain “__”.

I wouldn’t use this in production code, but it should get you started.


回答 6

这是我自己的不起眼的贡献-@HåvardS的高度评价的答案略有修饰,但略显一点(因此@ S.Lott可以接受,尽管可能对OP不够好):

import sys

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

class Wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        try:
            return getattr(self.wrapped, name)
        except AttributeError:
            return getattr(A(), name)

_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])

if __name__ == "__main__":
    _globals.salutation("world")

Here’s my own humble contribution — a slight embellishment of @Håvard S’s highly rated answer, but a bit more explicit (so it might be acceptable to @S.Lott, even though probably not good enough for the OP):

import sys

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

class Wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        try:
            return getattr(self.wrapped, name)
        except AttributeError:
            return getattr(A(), name)

_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])

if __name__ == "__main__":
    _globals.salutation("world")

回答 7

创建包含您的类的模块文件。导入模块。getattr在刚导入的模块上运行。您可以使用以下方式进行动态导入__import__ sys.modules中的模块。

这是您的模块some_module.py

class Foo(object):
    pass

class Bar(object):
    pass

在另一个模块中:

import some_module

Foo = getattr(some_module, 'Foo')

动态地执行此操作:

import sys

__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')

Create your module file that has your classes. Import the module. Run getattr on the module you just imported. You can do a dynamic import using __import__ and pull the module from sys.modules.

Here’s your module some_module.py:

class Foo(object):
    pass

class Bar(object):
    pass

And in another module:

import some_module

Foo = getattr(some_module, 'Foo')

Doing this dynamically:

import sys

__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')

在Mac上安装python模块的最兼容方法是什么?

问题:在Mac上安装python模块的最兼容方法是什么?

我开始学习python并热爱它。我主要在Mac和Linux上工作。我发现在Linux(主要是Ubuntu 9.04)上,使用apt-get安装python模块时,它工作正常。我可以轻松导入它。

在Mac上,我习惯于使用Macports安装所有Unixy东西。但是,我发现我安装的大多数python模块都没有被python看到。我花了一些时间玩弄PATH设置并使用python_select。什么都没有真正起作用,在这一点上我还不是很了解,相反,我只是闲逛。

我得到的印象是Macports在管理python模块方面并不广受喜爱。我想使用更“接受”(如果正确的话)的方法重新开始。

因此,我想知道Mac python开发人员使用什么方法来管理其模块?

奖励问题:

您使用Apple的python还是其他版本?您是从源代码编译所有内容,还是有运行良好的软件包管理器(Fink?)。

I’m starting to learn python and loving it. I work on a Mac mainly as well as Linux. I’m finding that on Linux (Ubuntu 9.04 mostly) when I install a python module using apt-get it works fine. I can import it with no trouble.

On the Mac, I’m used to using Macports to install all the Unixy stuff. However, I’m finding that most of the python modules I install with it are not being seen by python. I’ve spent some time playing around with PATH settings and using python_select . Nothing has really worked and at this point I’m not really understanding, instead I’m just poking around.

I get the impression that Macports isn’t universally loved for managing python modules. I’d like to start fresh using a more “accepted” (if that’s the right word) approach.

So, I was wondering, what is the method that Mac python developers use to manage their modules?

Bonus questions:

Do you use Apple’s python, or some other version? Do you compile everything from source or is there a package manger that works well (Fink?).


回答 0

管理python软件包的最流行方法(如果您不使用系统软件包管理器)是使用setuptools和easy_install。它可能已经安装在您的系统上。像这样使用它:

easy_install django

easy_install使用Python软件包索引 ,这对于python开发人员来说是一个了不起的资源。环顾四周,看看有哪些软件包可用。

更好的选择是pip,它越来越受欢迎,因为它试图解决与easy_install相关的许多问题。Pip使用与easy_install相同的软件包存储库,但效果更好。真正唯一需要使用easy_install的时间是此命令:

easy_install pip

之后,使用:

pip install django

在某个时候,您可能需要学习有关virtualenv的知识。如果您在软件包需求冲突的项目上进行大量的python开发,那么virtualenv是天赐之物。它将允许您使用各种软件包的完全不同的版本,并根据需要在它们之间轻松切换。

关于使用哪个python,坚持使用Apple的python会让您头疼最少,但是如果您需要更新的版本(我相信Leopard是2.5.1),我会选择macports python 2.6。

The most popular way to manage python packages (if you’re not using your system package manager) is to use setuptools and easy_install. It is probably already installed on your system. Use it like this:

easy_install django

easy_install uses the Python Package Index which is an amazing resource for python developers. Have a look around to see what packages are available.

A better option is pip, which is gaining traction, as it attempts to fix a lot of the problems associated with easy_install. Pip uses the same package repository as easy_install, it just works better. Really the only time use need to use easy_install is for this command:

easy_install pip

After that, use:

pip install django

At some point you will probably want to learn a bit about virtualenv. If you do a lot of python development on projects with conflicting package requirements, virtualenv is a godsend. It will allow you to have completely different versions of various packages, and switch between them easily depending your needs.

Regarding which python to use, sticking with Apple’s python will give you the least headaches, but If you need a newer version (Leopard is 2.5.1 I believe), I would go with the macports python 2.6.


回答 1

您的问题已经三岁了,有些其他答案未涵盖的细节:

我认识的大多数人都使用HomeBrewMacPorts,因此我更喜欢MacPorts,因为它清晰地默认了Mac OS X环境和我的开发设置。只需移出/ opt文件夹并使用普通用户Python环境测试您的软件包即可

MacPorts仅可在Mac中移植,但是通过easy_install或pip,您将学习如何在任何平台(Win / Mac / Linux / Bsd …)上设置环境。此外,它将始终是最新的,并且具有更多的软件包

我个人让MacPorts处理我的Python模块以保持所有更新。像任何其他高级软件包管理器(即apt-get)一样,对于具有大量二进制相关性的繁重模块而言,这要好得多。我无法用easy_install或pip构建Qt绑定(PySide)。Qt非常庞大,需要大量编译。一旦您想要一个需要非Python程序使用的库的Python包,请尝试避免easy_install或pip

在某些时候,您会发现MacPorts中缺少某些软件包。我不相信MacPorts会为您提供整个CheeseShop。例如,最近我需要Elixir模块,但是MacPorts仅提供py25-elixir和py26-elixir,没有py27版本。在这种情况下,您可以:

pip-2.7安装–user elixir

(确保您总是输入pip-(version))

这将在您的主目录中构建一个额外的Python库。是的,Python将在一个以上的库位置中工作:一个库由MacPorts控制,而一个用户本地库则用于MacPorts中缺少的所有内容。

现在注意,我更喜欢pip而不是easy_install。有充分的理由应该避免使用setuptools和easy_install。这是一个很好的解释,我尝试远离它们。pip的一项非常有用的功能是为您提供与MacPorts,easy_install和pip本身一起安装的所有模块(及其版本)的列表:

pip-2.7冻结

如果您已经开始使用easy_install,请放心,pip可以识别easy_install已经完成的所有操作,甚至可以升级随其安装的软件包。

如果您是开发人员,请密切注意virtualenv,以控制不同的设置和模块版本的组合。其他答案已经提到了它,到目前为止没有提到的是Tox模块,该工具用于测试您的软件包是否可以在不同的Python版本中正确安装。

尽管我通常没有版本冲突,但我还是喜欢virtualenv来设置一个干净的环境并清晰地查看我的软件包依赖项。这样,我就永远不会忘记setup.py中的任何依赖项

如果您选择MacPorts,请注意,不再选择同一软件包的多个版本,例如带有额外python_select软件包的旧Debian风格(出于兼容性,它仍然存在)。现在,您可以使用select命令选择将使用哪个Python版本(甚至可以选择Apple安装的版本):

$  port select python
Available versions for python:
    none
    python25-apple
    python26-apple
    python27 (active)
    python27-apple
    python32

$ port select python python32

在它上面添加tox,您的程序应该真正可移植

Your question is already three years old and there are some details not covered in other answers:

Most people I know use HomeBrew or MacPorts, I prefer MacPorts because of its clean cut of what is a default Mac OS X environment and my development setup. Just move out your /opt folder and test your packages with a normal user Python environment

MacPorts is only portable within Mac, but with easy_install or pip you will learn how to setup your environment in any platform (Win/Mac/Linux/Bsd…). Furthermore it will always be more up to date and with more packages

I personally let MacPorts handle my Python modules to keep everything updated. Like any other high level package manager (ie: apt-get) it is much better for the heavy lifting of modules with lots of binary dependencies. There is no way I would build my Qt bindings (PySide) with easy_install or pip. Qt is huge and takes a lot to compile. As soon as you want a Python package that needs a library used by non Python programs, try to avoid easy_install or pip

At some point you will find that there are some packages missing within MacPorts. I do not believe that MacPorts will ever give you the whole CheeseShop. For example, recently I needed the Elixir module, but MacPorts only offers py25-elixir and py26-elixir, no py27 version. In cases like these you have:

pip-2.7 install –user elixir

( make sure you always type pip-(version) )

That will build an extra Python library in your home dir. Yes, Python will work with more than one library location: one controlled by MacPorts and a user local one for everything missing within MacPorts.

Now notice that I favor pip over easy_install. There is a good reason you should avoid setuptools and easy_install. Here is a good explanation and I try to keep away from them. One very useful feature of pip is giving you a list of all the modules (along their versions) that you installed with MacPorts, easy_install and pip itself:

pip-2.7 freeze

If you already started using easy_install, don’t worry, pip can recognize everything done already by easy_install and even upgrade the packages installed with it.

If you are a developer keep an eye on virtualenv for controlling different setups and combinations of module versions. Other answers mention it already, what is not mentioned so far is the Tox module, a tool for testing that your package installs correctly with different Python versions.

Although I usually do not have version conflicts, I like to have virtualenv to set up a clean environment and get a clear view of my packages dependencies. That way I never forget any dependencies in my setup.py

If you go for MacPorts be aware that multiple versions of the same package are not selected anymore like the old Debian style with an extra python_select package (it is still there for compatibility). Now you have the select command to choose which Python version will be used (you can even select the Apple installed ones):

$  port select python
Available versions for python:
    none
    python25-apple
    python26-apple
    python27 (active)
    python27-apple
    python32

$ port select python python32

Add tox on top of it and your programs should be really portable


回答 2

请参阅Python OS X开发环境。最好的方法是使用MacPorts。下载并安装MacPorts,然后通过在终端中键入以下命令来通过MacPorts安装Python:

须藤端口安装python26 python_select
sudo端口选择--set python python26

要么

须藤端口安装python30 python_select
sudo端口选择--set python python30

使用第一组命令安装Python 2.6,使用第二组命令安装Python 3.0。然后使用:

sudo端口安装py26-packagename

要么

sudo端口安装py30-packagename

在上述命令中,将packagename替换为软件包的名称,例如:

sudo端口安装py26-setuptools

这些命令将自动为给定的Python版本安装软件包(及其依赖项)。

有关Python可用软件包的完整列表,请键入:

端口列表| grep py26-

要么

端口列表| grep py30-

您使用哪个命令取决于您选择安装的Python版本。

Please see Python OS X development environment. The best way is to use MacPorts. Download and install MacPorts, then install Python via MacPorts by typing the following commands in the Terminal:

sudo port install python26 python_select
sudo port select --set python python26

OR

sudo port install python30 python_select
sudo port select --set python python30

Use the first set of commands to install Python 2.6 and the second set to install Python 3.0. Then use:

sudo port install py26-packagename

OR

sudo port install py30-packagename

In the above commands, replace packagename with the name of the package, for example:

sudo port install py26-setuptools

These commands will automatically install the package (and its dependencies) for the given Python version.

For a full list of available packages for Python, type:

port list | grep py26-

OR

port list | grep py30-

Which command you use depends on which version of Python you chose to install.


回答 3

我使用MacPorts将Python和MacPorts跟踪的任何第三方模块/opt/local安装到中,并且将任何手动安装的模块(不在MacPorts存储库中的模块)安装到中/usr/local,这从来没有引起任何问题。我认为您可能对某些MacPorts脚本和环境变量的使用感到困惑。

MacPorts python_select用于选择“当前”版本的Python,但与模块无关。例如,这允许您使用MacPorts安装Python 2.5和Python 2.6,并在安装之间进行切换。

$PATH环境变量并没有影响Python模块被加载的内容。$PYTHONPATH是您要寻找的。$PYTHONPATH应该指向包含要加载的Python模块的目录。就我而言,我的$PYTHONPATH变量包含/usr/local/lib/python26/site-packages。如果您使用MacPorts的Python,它将为您设置其他适当的目录,因此您只需向添加其他路径$PYTHONPATH。但是同样,$PATH当Python搜索您已安装的模块时,根本不使用它。

$PATH 用于查找可执行文件,所以如果你安装的MacPorts’的Python,确保/opt/local/bin在你的$PATH

I use MacPorts to install Python and any third-party modules tracked by MacPorts into /opt/local, and I install any manually installed modules (those not in the MacPorts repository) into /usr/local, and this has never caused any problems. I think you may be confused as to the use of certain MacPorts scripts and environment variables.

MacPorts python_select is used to select the “current” version of Python, but it has nothing to do with modules. This allows you to, e.g., install both Python 2.5 and Python 2.6 using MacPorts, and switch between installs.

The $PATH environment variables does not affect what Python modules are loaded. $PYTHONPATH is what you are looking for. $PYTHONPATH should point to directories containing Python modules you want to load. In my case, my $PYTHONPATH variable contains /usr/local/lib/python26/site-packages. If you use MacPorts’ Python, it sets up the other proper directories for you, so you only need to add additional paths to $PYTHONPATH. But again, $PATH isn’t used at all when Python searches for modules you have installed.

$PATH is used to find executables, so if you install MacPorts’ Python, make sure /opt/local/bin is in your $PATH.


回答 4

使用MacPorts Python安装没有任何问题。如果要从MacPorts安装python模块,但看不到它们,则可能意味着您不调用安装的MacPorts python。在终端外壳中,您可以使用绝对路径来调用可能安装的各种Python。例如:

$ /usr/bin/python2.5         # Apple-supplied 2.5 (Leopard)
$ /opt/local/bin/python2.5   # MacPorts 2.5
$ /opt/local/bin/python2.6   # MacPorts 2.6
$ /usr/local/bin/python2.6   # python.org (MacPython) 2.6
$ /usr/local/bin/python3.1   # python.org (MacPython) 3.1

要在默认情况下获取正确的python,需要确保您的shell $ PATH设置正确,以确保首先找到正确的可执行文件。另一个解决方案是为各种python定义shell别名。

就像其他人建议的那样,也可以使用python.org(MacPython)安装。 easy_install 同样可以提供帮助,但是,由于每个Python实例可能都有自己的easy_install命令,因此请确保您调用的是right easy_install

There’s nothing wrong with using a MacPorts Python installation. If you are installing python modules from MacPorts but then not seeing them, that likely means you are not invoking the MacPorts python you installed to. In a terminal shell, you can use absolute paths to invoke the various Pythons that may be installed. For example:

$ /usr/bin/python2.5         # Apple-supplied 2.5 (Leopard)
$ /opt/local/bin/python2.5   # MacPorts 2.5
$ /opt/local/bin/python2.6   # MacPorts 2.6
$ /usr/local/bin/python2.6   # python.org (MacPython) 2.6
$ /usr/local/bin/python3.1   # python.org (MacPython) 3.1

To get the right python by default requires ensuring your shell $PATH is set properly to ensure that the right executable is found first. Another solution is to define shell aliases to the various pythons.

A python.org (MacPython) installation is fine, too, as others have suggested. easy_install can help but, again, because each Python instance may have its own easy_install command, make sure you are invoking the right easy_install.


回答 5

如果您从MacPorts使用Python,则它具有自己的easy_install,位于:/opt/local/bin/easy_install-2.6(对于py26而言)。这与简单地直接调用easy_install不同,即使您使用python_select更改了默认的python命令也是如此。

If you use Python from MacPorts, it has it’s own easy_install located at: /opt/local/bin/easy_install-2.6 (for py26, that is). It’s not the same one as simply calling easy_install directly, even if you used python_select to change your default python command.


回答 6

您有没有研究过easy_install?它不会同步您的macports或类似的东西,但是会自动下载最新的软件包和所有必要的依赖项,即

easy_install nose

用于鼻子单元测试包,或

easy_install trac

对于tracbug跟踪系统。

他们的EasyInstall页面上也有更多信息。

Have you looked into easy_install at all? It won’t synchronize your macports or anything like that, but it will automatically download the latest package and all necessary dependencies, i.e.

easy_install nose

for the nose unit testing package, or

easy_install trac

for the trac bug tracker.

There’s a bit more information on their EasyInstall page too.


回答 7

对于MacPython安装,我在此博客文章中找到了一种用于解决setuptools(easy_install)问题的有效解决方案:

http://droidism.com/getting-running-with-django-and-macpython-26-on-leopard

一个方便的技巧包括找出终端中哪个版本的python是活动的:

which python

For MacPython installations, I found an effective solution to fixing the problem with setuptools (easy_install) in this blog post:

http://droidism.com/getting-running-with-django-and-macpython-26-on-leopard

One handy tip includes finding out which version of python is active in the terminal:

which python

回答 8

使用MacPorts安装模块时,它不会进入Apple的Python版本。而是将这些模块安装到所选的MacPorts版本的Python中。

您可以使用名为python_select的mac端口更改默认使用的Python版本。这里的说明。

另外,还有easy_install。它将使用python安装python模块。

When you install modules with MacPorts, it does not go into Apple’s version of Python. Instead those modules are installed onto the MacPorts version of Python selected.

You can change which version of Python is used by default using a mac port called python_select. instructions here.

Also, there’s easy_install. Which will use python to install python modules.


回答 9

您可能已经pip3预先安装了,请尝试一下!

You may already have pip3 pre-installed, so just try it!


回答 10

关于使用哪个python版本,Mac OS通常会附带一个旧版本的python。升级到新版本是一个好主意。您可以从http://www.python.org/download/下载.dmg 。如果这样做,请记住更新路径。您可以在此处找到确切的命令http://farmdev.com/thoughts/66/python-3-0-on-mac-os-x-alongside-2-6-2-5-etc-/

Regarding which python version to use, Mac OS usually ships an old version of python. It’s a good idea to upgrade to a newer version. You can download a .dmg from http://www.python.org/download/ . If you do that, remember to update the path. You can find the exact commands here http://farmdev.com/thoughts/66/python-3-0-on-mac-os-x-alongside-2-6-2-5-etc-/


回答 11

我将easy_install与Apple的Python配合使用,它的工作原理很吸引人。

I use easy_install with Apple’s Python, and it works like a charm.


回答 12

直接安装其中一个fink软件包(2013年11月开始的Django 1.6)

fink install django-py27
fink install django-py33

或者创建一个虚拟环境:

fink install virtualenv-py27
virtualenv django-env
source django-env/bin/activate
pip install django
deactivate # when you are done

或者在virtualenv中使用fink django以及任何其他pip安装的软件包

fink install django-py27
fink install virtualenv-py27
virtualenv django-env --system-site-packages
source django-env/bin/activate
# django already installed
pip install django-analytical # or anything else you might want
deactivate # back to your normally scheduled programming

Directly install one of the fink packages (Django 1.6 as of 2013-Nov)

fink install django-py27
fink install django-py33

Or create yourself a virtualenv:

fink install virtualenv-py27
virtualenv django-env
source django-env/bin/activate
pip install django
deactivate # when you are done

Or use fink django plus any other pip installed packages in a virtualenv

fink install django-py27
fink install virtualenv-py27
virtualenv django-env --system-site-packages
source django-env/bin/activate
# django already installed
pip install django-analytical # or anything else you might want
deactivate # back to your normally scheduled programming

如何制作跨模块变量?

问题:如何制作跨模块变量?

__debug__变量很方便,部分原因是它会影响每个模块。如果我想创建另一个工作方式相同的变量,我该怎么做?

变量(让我们成为原始变量,并将其称为“ foo”)不必是真正的全局变量,在某种意义上,如果我在一个模块中更改foo,则在其他模块中对其进行更新。如果我可以在导入其他模块之前设置foo,然后它们会看到相同的值,那很好。

The __debug__ variable is handy in part because it affects every module. If I want to create another variable that works the same way, how would I do it?

The variable (let’s be original and call it ‘foo’) doesn’t have to be truly global, in the sense that if I change foo in one module, it is updated in others. I’d be fine if I could set foo before importing other modules and then they would see the same value for it.


回答 0

我不以任何方式,形状或形式认可该解决方案。但是,如果您将变量添加到__builtin__模块中,则__builtin__默认情况下,就如同所有其他包含- 的模块一样,都可以访问该变量。

a.py包含

print foo

b.py包含

import __builtin__
__builtin__.foo = 1
import a

结果是打印了“ 1”。

编辑:__builtin__模块可用作本地符号__builtins__-这就是其中两个答案之间存在差异的原因。另请注意,__builtin__它已builtins在python3中重命名为。

I don’t endorse this solution in any way, shape or form. But if you add a variable to the __builtin__ module, it will be accessible as if a global from any other module that includes __builtin__ — which is all of them, by default.

a.py contains

print foo

b.py contains

import __builtin__
__builtin__.foo = 1
import a

The result is that “1” is printed.

Edit: The __builtin__ module is available as the local symbol __builtins__ — that’s the reason for the discrepancy between two of these answers. Also note that __builtin__ has been renamed to builtins in python3.


回答 1

如果您需要一个全局的跨模块变量,也许只需简单的全局模块级变量就足够了。

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

测试:

$ python b.py
# -> 1 2

实际示例:Django的global_settings.py(尽管在Django应用中,设置是通过导入对象使用的 django.conf.settings)。

If you need a global cross-module variable maybe just simple global module-level variable will suffice.

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

Test:

$ python b.py
# -> 1 2

Real-world example: Django’s global_settings.py (though in Django apps settings are used by importing the object django.conf.settings).


回答 2

定义一个模块(称为“ globalbaz”)并在其中定义变量。使用此“ pseudoglobal”的所有模块都应导入“ globalbaz”模块,并使用“ globalbaz.var_name”进行引用

无论更改的位置如何,此方法均有效,您可以在导入之前或之后更改变量。导入的模块将使用最新值。(我在一个玩具示例中对此进行了测试)

为了澄清起见,globalbaz.py看起来像这样:

var_name = "my_useful_string"

Define a module ( call it “globalbaz” ) and have the variables defined inside it. All the modules using this “pseudoglobal” should import the “globalbaz” module, and refer to it using “globalbaz.var_name”

This works regardless of the place of the change, you can change the variable before or after the import. The imported module will use the latest value. (I tested this in a toy example)

For clarification, globalbaz.py looks just like this:

var_name = "my_useful_string"

回答 3

我认为在很多情况下它确实有意义,并且它简化了编程,使某些全局变量在多个(紧密耦合的)模块中广为人知。本着这种精神,我想详细说明一下由需要引用全局模块的模块导入的全局模块。

当只有一个这样的模块时,我将其命名为“ g”。在其中,我为每个要视为全局变量的变量分配默认值。在使用它们的每个模块中,我都不使用“ from g import var”,因为这只会导致局部变量,该局部变量仅在导入时才从g初始化。我以g.var和“ g”的形式进行大多数引用。不断提醒我,我正在处理其他模块可能访问的变量。

如果此类全局变量的值要在模块中的某些函数中频繁使用,则该函数可以创建本地副本:var = g.var。但是,重要的是要认识到对var的分配是本地的,并且全局g.var不能在不显式引用分配中的g.var的情况下进行更新。

请注意,您还可以让模块的不同子集共享多个这样的全局变量模块,以使事情得到更严格的控制。我对全局模块使用短名称的原因是为了避免由于出现它们而使代码过于混乱。仅凭少量经验,它们仅用1个或2个字符就变得足够易记。

当x尚未在g中定义时,仍然可以对gx进行赋值,然后另一个模块可以访问gx。但是,即使解释器允许,这种方法也不是那么透明,我会避免它。由于赋值的变量名称中有错字,仍有可能意外地在g中创建新变量。有时,对dir(g)的检查对于发现此类事故可能引起的任何意外名称很有用。

I believe that there are plenty of circumstances in which it does make sense and it simplifies programming to have some globals that are known across several (tightly coupled) modules. In this spirit, I would like to elaborate a bit on the idea of having a module of globals which is imported by those modules which need to reference them.

When there is only one such module, I name it “g”. In it, I assign default values for every variable I intend to treat as global. In each module that uses any of them, I do not use “from g import var”, as this only results in a local variable which is initialized from g only at the time of the import. I make most references in the form g.var, and the “g.” serves as a constant reminder that I am dealing with a variable that is potentially accessible to other modules.

If the value of such a global variable is to be used frequently in some function in a module, then that function can make a local copy: var = g.var. However, it is important to realize that assignments to var are local, and global g.var cannot be updated without referencing g.var explicitly in an assignment.

Note that you can also have multiple such globals modules shared by different subsets of your modules to keep things a little more tightly controlled. The reason I use short names for my globals modules is to avoid cluttering up the code too much with occurrences of them. With only a little experience, they become mnemonic enough with only 1 or 2 characters.

It is still possible to make an assignment to, say, g.x when x was not already defined in g, and a different module can then access g.x. However, even though the interpreter permits it, this approach is not so transparent, and I do avoid it. There is still the possibility of accidentally creating a new variable in g as a result of a typo in the variable name for an assignment. Sometimes an examination of dir(g) is useful to discover any surprise names that may have arisen by such accident.


回答 4

您可以将一个模块的全局变量传递给另一个模块:

在模块A中:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

在模块B中:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3

You can pass the globals of one module to onother:

In Module A:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

In Module B:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3

回答 5

全局变量通常不是一个好主意,但是您可以通过分配给__builtins__

__builtins__.foo = 'something'
print foo

同样,模块本身是可以从任何模块访问的变量。因此,如果您定义一个名为的模块my_globals.py

# my_globals.py
foo = 'something'

然后,您也可以在任何地方使用它:

import my_globals
print my_globals.foo

__builtins__通常,使用模块而不是修改模块是执行此类全局操作的更干净的方法。

Global variables are usually a bad idea, but you can do this by assigning to __builtins__:

__builtins__.foo = 'something'
print foo

Also, modules themselves are variables that you can access from any module. So if you define a module called my_globals.py:

# my_globals.py
foo = 'something'

Then you can use that from anywhere as well:

import my_globals
print my_globals.foo

Using modules rather than modifying __builtins__ is generally a cleaner way to do globals of this sort.


回答 6

您已经可以使用模块级变量执行此操作。无论从哪个模块导入模块,它们都是相同的。因此,您可以在将其放入,访问或从其他模块分配给它的任何有意义的模块中,将该变量设为模块级变量。最好调用一个函数来设置变量的值,或者使其成为某个单例对象的属性。这样,如果您最终需要在更改变量后运行一些代码,则可以这样做而不会破坏模块的外部接口。

通常,这不是一种很好的处理方法-很少使用全局变量-但我认为这是最干净的方法。

You can already do this with module-level variables. Modules are the same no matter what module they’re being imported from. So you can make the variable a module-level variable in whatever module it makes sense to put it in, and access it or assign to it from other modules. It would be better to call a function to set the variable’s value, or to make it a property of some singleton object. That way if you end up needing to run some code when the variable’s changed, you can do so without breaking your module’s external interface.

It’s not usually a great way to do things — using globals seldom is — but I think this is the cleanest way to do it.


回答 7

我想发布一个答案,在某些情况下找不到该变量。

循环导入可能会破坏模块的行为。

例如:

第一.py

import second
var = 1

第二个

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

在这个例子中,它应该很明显,但是在大型代码库中,这确实很令人困惑。

I wanted to post an answer that there is a case where the variable won’t be found.

Cyclical imports may break the module behavior.

For example:

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

On this is example it should be obvious, but in a large code-base, this can be really confusing.


回答 8

这听起来像修改__builtin__命名空间。去做吧:

import __builtin__
__builtin__.foo = 'some-value'

不要__builtins__直接使用(请注意额外的“ s”)-显然这可以是字典或模块。感谢ΤZnΩΤZΙΟΥ指出这一点,更多内容请点击这里

现在 foo可在任何地方使用。

我不建议一般这样做,但是使用此方法取决于程序员。

分配它必须按照上面的步骤进行,只是设置foo = 'some-other-value'只会在当前命名空间中进行设置。

This sounds like modifying the __builtin__ name space. To do it:

import __builtin__
__builtin__.foo = 'some-value'

Do not use the __builtins__ directly (notice the extra “s”) – apparently this can be a dictionary or a module. Thanks to ΤΖΩΤΖΙΟΥ for pointing this out, more can be found here.

Now foo is available for use everywhere.

I don’t recommend doing this generally, but the use of this is up to the programmer.

Assigning to it must be done as above, just setting foo = 'some-other-value' will only set it in the current namespace.


回答 9

我将此用于几个内置的原始函数,我觉得它们确实缺少了。一个示例是具有与filter,map,reduce相同的用法语义的find函数。

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

一旦运行(例如,通过在入口点附近导入),所有模块就可以使用find(),显然,它是内置的。

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

注意:当然,您可以使用过滤器和另一条线来测试零长度,或者使用减少一种奇怪的线来执行此操作,但是我始终觉得这很奇怪。

I use this for a couple built-in primitive functions that I felt were really missing. One example is a find function that has the same usage semantics as filter, map, reduce.

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

Once this is run (for instance, by importing near your entry point) all your modules can use find() as though, obviously, it was built in.

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

Note: You can do this, of course, with filter and another line to test for zero length, or with reduce in one sort of weird line, but I always felt it was weird.


回答 10

我可以使用字典来实现跨模块的可修改(或可变)变量:

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

启动时test_wait_app_up_fail,实际的超时时间为3秒。

I could achieve cross-module modifiable (or mutable) variables by using a dictionary:

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

When launching test_wait_app_up_fail, the actual timeout duration is 3 seconds.


回答 11

我想知道是否有可能避免使用全局变量的某些缺点(请参见例如http://wiki.c2.com/?GlobalVariablesAreBad通过使用类命名空间而不是全局/模块命名空间来传递变量值) 。以下代码表明这两种方法本质上是相同的。如下所述,使用类命名空间有一点优势。

以下代码片段还显示,可以在全局/模块命名空间和类命名空间中动态创建和删除属性或变量。

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

我称此模块为“墙”,因为它是用来反弹变量的。它将用作临时定义空类“路由器”的全局变量和类范围属性的空间。

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

该模块导入wall并定义一个函数sourcefn,该函数定义消息并通过两种不同的机制发出消息,一种通过全局机制,一种通过路由器函数。请注意,变量wall.msgwall.router.message是在此处首次在其各自的命名空间中定义。

目的地

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

该模块定义了一个函数destfn,该函数使用两种不同的机制来接收源发出的消息。它允许变量“ msg”可能不存在。destfn在显示变量后也将删除它们。

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

该模块依次调用先前定义的函数。第一次调用dest.destfn变量后wall.msgwall.router.msg不再存在。

该程序的输出为:

全球:您好,世界!
路由器:世界您好!
全局:无消息
路由器:无消息

上面的代码片段表明,模块/全局机制和类/类变量机制基本相同。

如果要共享许多变量,则可以通过使用多个wall类型的模块(例如wall1,wall2等)或通过在单个文件中定义多个路由器类型的类来管理命名空间污染。后者稍微更整洁,因此也许代表了使用类变量机制的边际优势。

I wondered if it would be possible to avoid some of the disadvantages of using global variables (see e.g. http://wiki.c2.com/?GlobalVariablesAreBad) by using a class namespace rather than a global/module namespace to pass values of variables. The following code indicates that the two methods are essentially identical. There is a slight advantage in using class namespaces as explained below.

The following code fragments also show that attributes or variables may be dynamically created and deleted in both global/module namespaces and class namespaces.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

I call this module ‘wall’ since it is used to bounce variables off of. It will act as a space to temporarily define global variables and class-wide attributes of the empty class ‘router’.

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

This module imports wall and defines a single function sourcefn which defines a message and emits it by two different mechanisms, one via globals and one via the router function. Note that the variables wall.msg and wall.router.message are defined here for the first time in their respective namespaces.

dest.py

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

This module defines a function destfn which uses the two different mechanisms to receive the messages emitted by source. It allows for the possibility that the variable ‘msg’ may not exist. destfn also deletes the variables once they have been displayed.

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

This module calls the previously defined functions in sequence. After the first call to dest.destfn the variables wall.msg and wall.router.msg no longer exist.

The output from the program is:

global: Hello world!
router: Hello world!
global: no message
router: no message

The above code fragments show that the module/global and the class/class variable mechanisms are essentially identical.

If a lot of variables are to be shared, namespace pollution can be managed either by using several wall-type modules, e.g. wall1, wall2 etc. or by defining several router-type classes in a single file. The latter is slightly tidier, so perhaps represents a marginal advantage for use of the class-variable mechanism.


在运行时检查Python模块版本

问题:在运行时检查Python模块版本

许多第三方Python模块都有一个属性,该属性保存该模块的版本信息(通常是module.VERSIONmodule.__version__),但是有些则没有。

此类模块的特定示例是libxslt和libxml2。

我需要检查这些模块在运行时是否使用了正确的版本。有没有办法做到这一点?

潜在的解决方案是在运行时读取源代码,对其进行哈希处理,然后将其与已知版本的哈希进行比较,但这很讨厌。

有更好的解决方案吗?

Many third-party Python modules have an attribute which holds the version information for the module (usually something like module.VERSION or module.__version__), however some do not.

Particular examples of such modules are libxslt and libxml2.

I need to check that the correct version of these modules are being used at runtime. Is there a way to do this?

A potential solution wold be to read in the source at runtime, hash it, and then compare it to the hash of the known version, but that’s nasty.

Is there a better solutions?


回答 0

我会远离哈希。使用的libxslt版本可能包含某种补丁,但不会影响您的使用。

作为一种替代方法,我建议您不要在运行时检查(不知道这是否很困难)。对于我编写的具有外部依赖性(第3方库)的python东西,我编写了一个脚本,用户可以运行该脚本来检查其python安装,以查看是否安装了适当的模块版本。

对于没有定义的“版本”属性的模块,您可以检查其包含的接口(类和方法),并查看它们是否与期望的接口匹配。然后,在您正在使用的实际代码中,假设第3方模块具有您期望的接口。

I’d stay away from hashing. The version of libxslt being used might contain some type of patch that doesn’t effect your use of it.

As an alternative, I’d like to suggest that you don’t check at run time (don’t know if that’s a hard requirement or not). For the python stuff I write that has external dependencies (3rd party libraries), I write a script that users can run to check their python install to see if the appropriate versions of modules are installed.

For the modules that don’t have a defined ‘version’ attribute, you can inspect the interfaces it contains (classes and methods) and see if they match the interface they expect. Then in the actual code that you’re working on, assume that the 3rd party modules have the interface you expect.


回答 1

使用pkg_resources。从PyPI安装的所有内容至少应具有版本号。

>>> import pkg_resources
>>> pkg_resources.get_distribution("blogofile").version
'0.7.1'

Use pkg_resources. Anything installed from PyPI at least should have a version number.

>>> import pkg_resources
>>> pkg_resources.get_distribution("blogofile").version
'0.7.1'

回答 2

一些想法:

  1. 尝试检查所需版本中存在的功能或不存在的功能。
  2. 如果没有函数差异,请检查函数参数和签名。
  3. 如果无法从函数签名中找出问题,请在导入时设置一些存根调用并检查其行为。

Some ideas:

  1. Try checking for functions that exist or don’t exist in your needed versions.
  2. If there are no function differences, inspect function arguments and signatures.
  3. If you can’t figure it out from function signatures, set up some stub calls at import time and check their behavior.

回答 3

您可以使用

pip freeze

以需求格式查看已安装的软件包。

You can use

pip freeze

to see the installed packages in requirements format.


回答 4

您可以importlib_metadata为此使用库。

如果您使用的是python < 3.8,请首先使用以下命令进行安装:

pip install importlib_metadata

从python开始,3.8它就包含在python的标准库中。

然后,要检查软件包的版本(在本示例中为lxml),请运行:

>>> from importlib_metadata import version
>>> version('lxml')
'4.3.1'

请记住,这仅适用于从PyPI安装的软件包。同样,您必须将包名称作为version方法的参数传递,而不是此包提供的模块名称(尽管它们通常是相同的)。

If you’re on python >=3.8 you can use a module from the built-in library for that. To check a package’s version (in this example lxml) run:

>>> from importlib.metadata import version
>>> version('lxml')
'4.3.1'

This functionality has been ported to older versions of python (<3.8) as well, but you need to install a separate library first:

pip install importlib_metadata

and then to check a package’s version (in this example lxml) run:

>>> from importlib_metadata import version
>>> version('lxml')
'4.3.1'

Keep in mind that this works only for packages installed from PyPI. Also, you must pass a package name as an argument to the version method, rather than a module name that this package provides (although they’re usually the same).


回答 5

我发现使用各种可用的工具(包括此其他答案中pkg_resources提到的最好的一种)非常不可靠,因为它们中的大多数都不能涵盖所有情况。例如

  • 内置模块
  • 未安装但仅添加到python路径的模块(例如,通过您的IDE)
  • 可以使用同一模块的两个版本(在python路径中取代已安装的一个)

由于我们需要一种可靠的方法来获取任何软件包,模块或子模块的版本,因此我最终编写了getversion。使用起来非常简单:

from getversion import get_module_version
import foo
version, details = get_module_version(foo)

有关详细信息,请参见文档

I found it quite unreliable to use the various tools available (including the best one pkg_resources mentioned by this other answer), as most of them do not cover all cases. For example

  • built-in modules
  • modules not installed but just added to the python path (by your IDE for example)
  • two versions of the same module available (one in python path superseding the one installed)

Since we needed a reliable way to get the version of any package, module or submodule, I ended up writing getversion. It is quite simple to use:

from getversion import get_module_version
import foo
version, details = get_module_version(foo)

See the documentation for details.


回答 6

对于不提供__version__以下功能但可以使用的模块:

#!/usr/bin/env python3.6
import sys
import os
import subprocess
import re

sp = subprocess.run(["pip3", "show", "numpy"], stdout=subprocess.PIPE)
ver = sp.stdout.decode('utf-8').strip().split('\n')[1]
res = re.search('^Version:\ (.*)$', ver)
print(res.group(1))

要么

#!/usr/bin/env python3.7
import sys
import os
import subprocess
import re

sp = subprocess.run(["pip3", "show", "numpy"], capture_output=True)
ver = sp.stdout.decode('utf-8').strip().split('\n')[1]
res = re.search('^Version:\ (.*)$', ver)
print(res.group(1))

For modules which do not provide __version__ the following is ugly but works:

#!/usr/bin/env python3.6
import sys
import os
import subprocess
import re

sp = subprocess.run(["pip3", "show", "numpy"], stdout=subprocess.PIPE)
ver = sp.stdout.decode('utf-8').strip().split('\n')[1]
res = re.search('^Version:\ (.*)$', ver)
print(res.group(1))

or

#!/usr/bin/env python3.7
import sys
import os
import subprocess
import re

sp = subprocess.run(["pip3", "show", "numpy"], capture_output=True)
ver = sp.stdout.decode('utf-8').strip().split('\n')[1]
res = re.search('^Version:\ (.*)$', ver)
print(res.group(1))

Python在同一文件夹中找不到模块

问题:Python在同一文件夹中找不到模块

我的python以某种方式无法在同一目录中找到任何模块。我究竟做错了什么?(python2.7)

所以我有一个目录“ 2014_07_13_test”,其中有两个文件:

  1. test.py
  2. 你好

在hello.py:

# !/usr/local/bin/python
# -*- coding: utf-8 -*-

def hello1():
    print 'HelloWorld!'

和test.py:

# !/usr/local/bin/python
# -*- coding: utf-8 -*-

from hello import hello1

hello1()

还是python给我

>>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 4, in <module>
ImportError: No module named hello

怎么了?

My python somehow can’t find any modules in the same directory. What am I doing wrong? (python2.7)

So I have one directory ‘2014_07_13_test’, with two files in it:

  1. test.py
  2. hello.py

where hello.py:

# !/usr/local/bin/python
# -*- coding: utf-8 -*-

def hello1():
    print 'HelloWorld!'

and test.py:

# !/usr/local/bin/python
# -*- coding: utf-8 -*-

from hello import hello1

hello1()

Still python gives me

>>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 4, in <module>
ImportError: No module named hello

What’s wrong?


回答 0

您的代码很好,我怀疑您的问题是如何启动它。

您需要从“ 2014_07_13_test”目录启动python。

打开命令提示符,然后将“ cd”进入您的“ 2014_07_13_test”目录。

例如:

$ cd /path/to/2014_07_13_test
$ python test.py

如果您无法像这样“ cd”进入目录,则可以将其添加到sys.path

在test.py中:

import sys, os
sys.path.append('/path/to/2014_07_13_test')

或设置/编辑PYTHONPATH

一切都应该很好…

…嗯,’shebang’行(两个文件的第一行)都存在轻微错误,’#’和’!’之间不应有空格。

有一个更好的家当,你应该使用。

另外,您不需要每个文件上的shebang行,只需要将您打算从Shell中运行的文件作为可执行文件。

Your code is fine, I suspect your problem is how you are launching it.

You need to launch python from your ‘2014_07_13_test’ directory.

Open up a command prompt and ‘cd’ into your ‘2014_07_13_test’ directory.

For instance:

$ cd /path/to/2014_07_13_test
$ python test.py

If you cannot ‘cd’ into the directory like this you can add it to sys.path

In test.py:

import sys, os
sys.path.append('/path/to/2014_07_13_test')

Or set/edit the PYTHONPATH

And all should be well…

…well there is a slight mistake with your ‘shebang’ lines (the first line in both your files), there shouldn’t be a space between the ‘#’ and the ‘!’

There is a better shebang you should use.

Also you don’t need the shebang line on every file… only the ones you intend to run from your shell as executable files.


回答 1

将您在test.py中的导入更改为:

from .hello import hello1

Change your import in test.py to:

from .hello import hello1

回答 2

我遇到了类似的问题,我通过将文件目录显式添加到路径列表中来解决了这个问题:

import os
import sys

file_dir = os.path.dirname(__file__)
sys.path.append(file_dir)

在那之后,我从同一目录导入没有问题。

I had a similar problem, I solved it by explicitly adding the file’s directory to the path list:

import os
import sys

file_dir = os.path.dirname(__file__)
sys.path.append(file_dir)

After that, I had no problem importing from the same directory.


回答 3

这是我使用的通用解决方案。它解决了从同一文件夹中的模块导入的问题:

import os.path
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

将其放在模块顶部,该模块将显示错误“没有名为xxxx的模块”

Here is the generic solution I use. It solves the problem for importing from modules in the same folder:

import os.path
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

Put this at top of the module which gives the error “No module named xxxx”


回答 4

就我而言,Python无法找到它,因为我会将代码放在带有连字符的模块中,例如my-module。当我将其更改为my_module工作时。

In my case, Python was unable to find it because I’d put the code inside a module with hyphens, e.g. my-module. When I changed it to my_module it worked.


回答 5

我遇到了这个问题。我在同一目录中有三个文件夹,因此必须指定哪个文件夹。例如:从文件夹导入脚本

I ran into this issue. I had three folders in the same directory so I had to specify which folder. Ex: from Folder import script


回答 6

以下内容不能解决OP的问题,但标题和错误正是我所面对的。

如果项目中包含setup.py脚本,则可以使用python3 -m pip install -e .python3 setup.py install或来安装您所在的软件包python3 setup.py develop,并且该软件包将被安装,但仍可编辑(因此,在导入软件包时将看到代码更改)。如果没有setup.py,请理解

无论如何,OP面临的问题似乎不再存在了?

文件one.py

def function():
    print("output")

文件two.py

#!/usr/bin/env python3

import one
one.function()
chmod +x two.py # To allow execution of the python file
./two.py # Only works if you have a python shebang

Command line output: output

其他解决方案似乎“肮脏”

对于带有2个测试文件的OP,将它们修改为可以正常工作。但是,在其他实际情况下,可能不建议使用其他答案中列出的方法。它们要求您修改python代码或限制灵活性(从特定目录运行python文件),并且通常会带来麻烦。如果您刚刚克隆了一个项目,然后发生这种情况怎么办?它可能已经对其他人有用,并且无需更改代码。选择的答案还希望人们从特定文件夹运行脚本以使其起作用。这可能是长期烦恼的根源,这永远都不是一件好事。它还建议您将特定的python文件夹添加到PATH(可以通过python或命令行完成)。同样,如果您在几个月后重命名或移动文件夹会发生什么?您必须再次搜寻该页面,并最终发现您需要设置路径(几个月前您确实做了此设置),而您只需要更新路径(确保可以使用sys.path并以编程方式设置它即可,但这仍然很不稳定) )。许多令人烦恼的来源。

The following doesn’t solve the OP’s problem, but the title and error is exactly what I faced.

If your project has a setup.py script in it, you can install that package you are in, with python3 -m pip install -e . or python3 setup.py install or python3 setup.py develop, and this package will be installed, but still editable (so changes to the code will be seen when importing the package). If it doesn’t have a setup.py, make sense of it.

Anyway, the problem OP faces seems to not exist anymore?

file one.py:

def function():
    print("output")

file two.py:

#!/usr/bin/env python3

import one
one.function()
chmod +x two.py # To allow execution of the python file
./two.py # Only works if you have a python shebang

Command line output: output

Other solutions seem ‘dirty’

In the case of OP with 2 test files, modifying them to work is probably fine. However, in other real scenarios, the methods listed in the other answers is probably not recommended. They require you to modify the python code or restrict your flexibility (running the python file from a specific directory) and generally introduce annoyances. What if you’ve just cloned a project, and this happens? It probably already works for other people, and making code changes is unnecessary. The chosen answer also wants people to run a script from a specific folder to make it work. This can be a source of long term annoyance, which is never good. It also suggests adding your specific python folder to PATH (can be done through python or command line). Again, what happens if you rename or move the folder in a few months? You have to hunt down this page again, and eventually discover you need to set the path (and that you did exactly this a few months ago), and that you simply need to update a path (sure you could use sys.path and programmatically set it, but this can be flaky still). Many sources of great annoyance.


是否使用-m选项执行Python代码

问题:是否使用-m选项执行Python代码

python解释器的-m 模块选项为“将库模块模块作为脚本运行”。

使用此python代码a.py:

if __name__ == "__main__":
    print __package__
    print __name__

我测试python -m a

"" <-- Empty String
__main__

python a.py回报

None <-- None
__main__

对我来说,这两个调用似乎是相同的,只是当使用-m选项调用__package__时不为None。

有趣的是,有了python -m runpy a,我得到了与python -m a编译成a.pyc的python模块相同的东西。

这些调用之间的(实际)区别是什么?他们之间有什么利弊吗?

同样,David Beazley的Python Essential Reference将其解释为“ -m选项将库模块作为脚本运行,该脚本在执行主脚本之前在__main__模块内部执行 ”。这是什么意思?

The python interpreter has -m module option that “Runs library module module as a script”.

With this python code a.py:

if __name__ == "__main__":
    print __package__
    print __name__

I tested python -m a to get

"" <-- Empty String
__main__

whereas python a.py returns

None <-- None
__main__

To me, those two invocation seems to be the same except __package__ is not None when invoked with -m option.

Interestingly, with python -m runpy a, I get the same as python -m a with python module compiled to get a.pyc.

What’s the (practical) difference between these invocations? Any pros and cons between them?

Also, David Beazley’s Python Essential Reference explains it as “The -m option runs a library module as a script which executes inside the __main__ module prior to the execution of the main script“. What does it mean?


回答 0

当您使用-m命令行标志时,Python将为您导入模块或包,然后将其作为脚本运行。当您不使用该-m标志时,您命名的文件仅作为脚本运行。

当您尝试运行软件包时,区别很重要。之间有很大的区别:

python foo/bar/baz.py

python -m foo.bar.baz

与后一种情况一样,foo.bar将导入,并且相对导入将foo.bar作为起点正确运行。

演示:

$ mkdir -p test/foo/bar
$ touch test/foo/__init__.py
$ touch test/foo/bar/__init__.py
$ cat << EOF > test/foo/bar/baz.py 
> if __name__ == "__main__":
>     print __package__
>     print __name__
> 
> EOF
$ PYTHONPATH=test python test/foo/bar/baz.py 
None
__main__
$ PYTHONPATH=test python -m foo.bar.baz 
foo.bar
__main__

结果,在使用-m开关时,Python实际上必须关心软件包。普通脚本永远不能是软件包,因此__package__将其设置为None

但运行一个封装或模块与包裹-m和现在至少存在可能性的封装的,所以__package__变量设置为一个字符串值; 在上面的演示中,将其设置为foo.bar,对于不在包内的普通模块,将其设置为空字符串。

至于__main__ 模块 ; Python会像常规模块一样导入正在运行的脚本。创建一个新的模块对象来保存存储在中的全局命名空间sys.modules['__main__']。这就是__name__变量所指的,它是该结构中的关键。

对于包,您可以创建一个__main__.py模块并在运行时让其运行python -m package_name;其实这是你的唯一途径可以运行包的脚本:

$ PYTHONPATH=test python -m foo.bar
python: No module named foo.bar.__main__; 'foo.bar' is a package and cannot be directly executed
$ cp test/foo/bar/baz.py test/foo/bar/__main__.py
$ PYTHONPATH=test python -m foo.bar
foo.bar
__main__

因此,在命名要与一起运行的包时-m,Python会查找__main__该包中包含的模块并将其作为脚本执行。然后,其名称仍设置为__main__,并且模块对象仍存储在中sys.modules['__main__']

When you use the -m command-line flag, Python will import a module or package for you, then run it as a script. When you don’t use the -m flag, the file you named is run as just a script.

The distinction is important when you try to run a package. There is a big difference between:

python foo/bar/baz.py

and

python -m foo.bar.baz

as in the latter case, foo.bar is imported and relative imports will work correctly with foo.bar as the starting point.

Demo:

$ mkdir -p test/foo/bar
$ touch test/foo/__init__.py
$ touch test/foo/bar/__init__.py
$ cat << EOF > test/foo/bar/baz.py 
> if __name__ == "__main__":
>     print __package__
>     print __name__
> 
> EOF
$ PYTHONPATH=test python test/foo/bar/baz.py 
None
__main__
$ PYTHONPATH=test python -m foo.bar.baz 
foo.bar
__main__

As a result, Python has to actually care about packages when using the -m switch. A normal script can never be a package, so __package__ is set to None.

But run a package or module inside a package with -m and now there is at least the possibility of a package, so the __package__ variable is set to a string value; in the above demonstration it is set to 'foo.bar', for plain modules not inside a package it is set to an empty string.

As for the __main__ module, Python imports scripts being run as it would import regular modules. A new module object is created to hold the global namespace and is stored in sys.modules['__main__']. This is what the __name__ variable refers to, it is a key in that structure.

For packages, you can create a __main__.py module inside and have that run when running python -m package_name; in fact that is the only way you can run a package as a script:

$ PYTHONPATH=test python -m foo.bar
python: No module named foo.bar.__main__; 'foo.bar' is a package and cannot be directly executed
$ cp test/foo/bar/baz.py test/foo/bar/__main__.py
$ PYTHONPATH=test python -m foo.bar
foo.bar
__main__

So, when naming a package for running with -m, Python looks for a __main__ module contained in that package and executes that as a script. Its name is then still set to '__main__' and the module object is still stored in sys.modules['__main__'].


回答 1

是否使用-m选项执行Python代码

使用-m标志。

当您拥有脚本时,结果几乎是相同的,但是当您开发一个没有-m标志的软件包时,如果您想运行软件包中的子软件包或模块作为主条目,则无法使导入正常工作指向您的程序(相信我,我已经尝试过了。)

该文档

就像-m标志上文档说的那样:

在sys.path中搜索指定的模块,并作为__main__模块执行其内容。

与-c选项一样,当前目录将添加到sys.path的开头。

所以

python -m pdb

大致相当于

python /usr/lib/python3.5/pdb.py

(假设您在当前目录中没有名为pdb.py的软件包或脚本)

说明:

使行为“故意类似于”脚本。

许多标准库模块包含在执行时作为脚本调用的代码。一个例子是timeit模块:

某些python代码旨在作为模块运行:(我认为此示例比命令行选项doc示例更好)

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop

并且从发行说明中突出显示了Python 2.4

-m命令行选项-python -m modulename将在标准库中找到一个模块,然后调用它。例如,python -m pdb 相当于python /usr/lib/python2.4/pdb.py

后续问题

同样,David Beazley的Python Essential Reference将其解释为“ -m选项将库模块作为脚本运行,该脚本__main__在执行主脚本之前在模块内部执行”。

这意味着您可以使用import语句查找的任何模块都可以作为程序的入口点运行-如果该模块具有代码块(通常在结尾处使用)if __name__ == '__main__':

-m 而不将当前目录添加到路径:

其他地方的评论说:

-m选项还将当前目录添加到sys.path中,显然是一个安全问题(请参阅:预加载攻击)。此行为类似于Windows中的库搜索顺序(之前已对其进行了强化)。很遗憾,Python没有遵循这种趋势,并且没有提供禁用添加的简单方法。到sys.path

好吧,这演示了可能的问题-(在Windows中删除引号):

echo "import sys; print(sys.version)" > pdb.py

python -m pdb
3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul  5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)]

使用该-I标志可将其锁定在生产环境中(版本3.4中的新增功能):

python -Im pdb
usage: pdb.py [-c command] ... pyfile [arg] ...
etc...

文档

-I

在隔离模式下运行Python。这也意味着-E和-s。在隔离模式下,sys.path既不包含脚本的目录也不包含用户的site-packages目录。所有PYTHON *环境变量也将被忽略。可能会施加进一步的限制,以防止用户注入恶意代码。

怎么__package__办?

它启用了显式相对导入,但与该问题并不特别相关-请在此处查看此答案:Python中“ __package__”属性的目的是什么?

Execution of Python code with -m option or not

Use the -m flag.

The results are pretty much the same when you have a script, but when you develop a package, without the -m flag, there’s no way to get the imports to work correctly if you want to run a subpackage or module in the package as the main entry point to your program (and believe me, I’ve tried.)

The docs

Like the docs on the -m flag say:

Search sys.path for the named module and execute its contents as the __main__ module.

and

As with the -c option, the current directory will be added to the start of sys.path.

so

python -m pdb

is roughly equivalent to

python /usr/lib/python3.5/pdb.py

(assuming you don’t have a package or script in your current directory called pdb.py)

Explanation:

Behavior is made “deliberately similar to” scripts.

Many standard library modules contain code that is invoked on their execution as a script. An example is the timeit module:

Some python code is intended to be run as a module: (I think this example is better than the commandline option doc example)

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop

And from the release note highlights for Python 2.4:

The -m command line option – python -m modulename will find a module in the standard library, and invoke it. For example, python -m pdb is equivalent to python /usr/lib/python2.4/pdb.py

Follow-up Question

Also, David Beazley’s Python Essential Reference explains it as “The -m option runs a library module as a script which executes inside the __main__ module prior to the execution of the main script”.

It means any module you can lookup with an import statement can be run as the entry point of the program – if it has a code block, usually near the end, with if __name__ == '__main__':.

-m without adding the current directory to the path:

A comment here elsewhere says:

That the -m option also adds the current directory to sys.path, is obviously a security issue (see: preload attack). This behavior is similar to library search order in Windows (before it had been hardened recently). It’s a pity that Python does not follow the trend and does not offer a simple way to disable adding . to sys.path

Well, this demonstrates the possible issue – (in windows remove the quotes):

echo "import sys; print(sys.version)" > pdb.py

python -m pdb
3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul  5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)]

Use the -I flag to lock this down for production environments (new in version 3.4):

python -Im pdb
usage: pdb.py [-c command] ... pyfile [arg] ...
etc...

from the docs:

-I

Run Python in isolated mode. This also implies -E and -s. In isolated mode sys.path contains neither the script’s directory nor the user’s site-packages directory. All PYTHON* environment variables are ignored, too. Further restrictions may be imposed to prevent the user from injecting malicious code.

What does __package__ do?

It enables explicit relative imports, not particularly germane to this question, though – see this answer here: What’s the purpose of the “__package__” attribute in Python?


回答 2

使用-m将模块(或程序包)作为脚本运行的主要原因是简化部署,尤其是在Windows上。您可以将脚本安装在模块通常可以使用的Python库中的同一位置-而不污染PATH或〜/ .local等全局可执行目录(在Windows中很难找到每个用户的脚本目录)。

然后,您只需键入-m,Python就会自动找到该脚本。例如,python -m pip将为执行它的同一Python解释器实例找到正确的点。如果没有-m,那么如果用户安装了多个Python版本,哪个是“全局” pip?

如果用户更喜欢命令行脚本的“经典”入口点,则可以轻松地将它们作为小脚本添加到PATH中的某个位置,或者pip可以在安装时使用setup.py中的entry_points参数创建它们。

因此,只需检查__name__ == '__main__'并忽略其他不可靠的实现细节。

The main reason to run a module (or package) as a script with -m is to simplify deployment, especially on Windows. You can install scripts in the same place in the Python library where modules normally go – instead of polluting PATH or global executable directories such as ~/.local (the per-user scripts directory is ridiculously hard to find in Windows).

Then you just type -m and Python finds the script automagically. For example, python -m pip will find the correct pip for the same instance of Python interpreter which executes it. Without -m, if user has several Python versions installed, which one would be the “global” pip?

If user prefers “classic” entry points for command-line scripts, these can be easily added as small scripts somewhere in PATH, or pip can create these at install time with entry_points parameter in setup.py.

So just check for __name__ == '__main__' and ignore other non-reliable implementation details.