模块加载如何在CPython下工作?特别是,用C编写的扩展的动态加载如何工作?我在哪里可以了解到这一点?
我发现源代码本身相当压倒性.我可以看到,可靠的ol' dlopen()和朋友用在支持它的系统上但没有任何大局观,从源代码中解决这个问题需要很长时间.
关于这个主题可以写很多,但据我所知,几乎没有任何内容 - 描述Python语言本身的大量网页使搜索变得困难.一个很好的答案将提供一个相当简短的概述和参考资源,我可以了解更多.
我最关心的是它是如何在类Unix系统上运行的,因为这就是我所知道的但我感兴趣的是如果这个过程在其他地方类似.
为了更具体(但风险假设太多),CPython如何使用模块方法表和初始化函数来"理解"动态加载的C?
python cpython dynamic-loading python-import python-internals
我写了一个简单的Eratosthenes筛子,它使用了一个列表,如果不是素数则将它们变成零,如下:
def eSieve(n): #Where m is fixed-length list of all integers up to n
'''Creates a list of primes less than or equal to n'''
m = [1]*(n+1)
for i in xrange(2,int((n)**0.5)+1):
if m[i]:
for j in xrange(i*i,n+1,i):
m[j]=0
return [i for i in xrange(2,n) if m[i]]
Run Code Online (Sandbox Code Playgroud)
我测试了它运行的速度%timeit并获得:
#n: t
#10**1: 7 ?s
#10**2: 26.6 ?s
#10**3: 234 ?s
#10**4: 2.46 ms
#10**5: 26.4 ms
#10**6: 292 ms
#10**7: 3.27 s
Run Code Online (Sandbox Code Playgroud)
我假设,如果我改变[1]并且0使用布尔值,它会跑得更快......但它恰恰相反:
#n: t …Run Code Online (Sandbox Code Playgroud) CPython 3.6.4:
from functools import partial
def add(x, y, z, a):
return x + y + z + a
list_of_as = list(range(10000))
def max1():
return max(list_of_as , key=lambda a: add(10, 20, 30, a))
def max2():
return max(list_of_as , key=partial(add, 10, 20, 30))
Run Code Online (Sandbox Code Playgroud)
现在:
In [2]: %timeit max1()
4.36 ms ± 42.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [3]: %timeit max2()
3.67 ms ± 25.9 µs per loop (mean ± std. dev. of …Run Code Online (Sandbox Code Playgroud) 我知道,str()方法的目的是返回一个对象的字符串表示,所以我想测试如果我强迫它做其他事情会发生什么.
我创建了一个类和一个对象:
class MyClass(object):
def __str__(self, a=2, b=3):
return a + b
mc = MyClass()
Run Code Online (Sandbox Code Playgroud)
我打电话的时候:
print(str(mc))
Run Code Online (Sandbox Code Playgroud)
口译员抱怨说:
TypeError: __str__ returned non-string (type int)
Run Code Online (Sandbox Code Playgroud)
这是完全可以理解的,因为str()方法试图返回int.
但如果我尝试:
print(mc.__str__())
Run Code Online (Sandbox Code Playgroud)
我得到输出:5.
那么为什么解释器允许我在__str__直接调用时返回int ,而不是当我使用str(mc)时 - 正如我所理解的那样 - 也被评估为mc.__str__().
如果我们看看 50k 元素以下的集合的调整大小行为:
>>> import sys
>>> s = set()
>>> seen = {}
>>> for i in range(50_000):
... size = sys.getsizeof(s)
... if size not in seen:
... seen[size] = len(s)
... print(f"{size=} {len(s)=}")
... s.add(i)
...
size=216 len(s)=0
size=728 len(s)=5
size=2264 len(s)=19
size=8408 len(s)=77
size=32984 len(s)=307
size=131288 len(s)=1229
size=524504 len(s)=4915
size=2097368 len(s)=19661
Run Code Online (Sandbox Code Playgroud)
此模式与一旦集合已满 3/5 后将后备存储大小翻四倍,再加上一些可能恒定的开销是一致的PySetObject:
>>> for i in range(9, 22, 2):
... print(2**i + 216)
...
728
2264
8408
32984
131288
524504 …Run Code Online (Sandbox Code Playgroud) 使用MinGW32一台Windows PC,我试图编译在pySpotify.第一个错误libspotify/api.h就是缺失了.我固定从复制相应的文件夹这个libspotify成C:\MinGW\include.但是现在dllwrap现在失败了ld linking..二进制文件Spotify分发是libspotify.dll和libspotify.lib.无论我把它们放在哪里(pySpotify文件夹/子文件夹,临时构建文件夹/子文件夹和MinGW文件夹/子文件夹)或我命名它们(.a,.o和.so)它仍显示相同的错误消息.
相关的错误是:
C:\MinGW\bin\dllwrap.exe -mdll -static --output-lib build\temp.win32-2.7\Release\src\lib_spotify.a --def build\temp.win32-2.7\Release\src\_spotify.def -s build\temp.win32-2.7\Release\src\module.o build\temp.win32-2.7\Release\src\session.o build\temp.win32-2.7\Release\src\link.o build\temp.win32-2.7\Release\src\track.obuild\temp.win32-2.7\Release\src\album.o build\temp.win32-2.7\Release\src\albumbrowser.o build\temp.win32-2.7\Release\src\artist.o build\temp.win32-2.7\Release\src\artistbrowser.o build\temp.win32-2.7\Release\src\search.o build\temp.win32-2.7\Release\src\playlist.o build\temp.win32-2.7\Release\src\playlistcontainer.o build\temp.win32-2.7\Release\src\playlistfolder.o build\temp.win32-2.7\Release\src\image.o build\temp.win32-2.7\Release\src\user.o build\temp.win32-2.7\Release\src\pyspotify.o build\temp.win32-2.7\Release\src\toplistbrowser.o -LC:\Python26\libs -LC:\Python26\PCbuild -lspotify -lpython26 -lmsvcr90 -o build\lib.win32-2.7\spotify\_spotify.pyd
c:/mingw/bin/../lib/gcc/mingw32/4.7.2/../../../../mingw32/bin/ld.exe: cannot find -lspotify
collect2.exe: error: ld returned 1 exit status
dllwrap: gcc exited with status 1
error: command …Run Code Online (Sandbox Code Playgroud) 在研究Python和C++之间的性能折衷的同时,我设计了一个小例子,主要关注一个愚蠢的子串匹配.
这是相关的C++:
using std::string;
std::vector<string> matches;
std::copy_if(patterns.cbegin(), patterns.cend(), back_inserter(matches),
[&fileContents] (const string &pattern) { return fileContents.find(pattern) != string::npos; } );
Run Code Online (Sandbox Code Playgroud)
以上是用-O3构建的.
这是Python:
def getMatchingPatterns(patterns, text):
return filter(text.__contains__, patterns)
Run Code Online (Sandbox Code Playgroud)
它们都采用大量模式和输入文件,并使用哑子搜索将模式列表过滤到文件中找到的模式列表.
版本是:
令我惊讶的是表现.我在低规格的Ubuntu上运行,Python的速度提高了大约20%.在具有cygwin的中型PC上也是如此 - Python速度提高了两倍.Profiler显示99 +%的周期用于字符串匹配(字符串复制和列表推导是无关紧要的).
显然,Python实现是本机C,我希望它与C++大致相同,但并不期望它快.
与gcc相比,对相关CPython优化的任何见解都是最受欢迎的.
作为参考,这里是完整的例子.输入只需要一组50K HTLM(每次测试都从磁盘读取,没有特殊的缓存):
蟒蛇:
import sys
def getMatchingPatterns(patterns, text):
return filter(text.__contains__, patterns)
def serialScan(filenames, patterns):
return zip(filenames, [getMatchingPatterns(patterns, open(filename).read()) for filename in filenames])
if __name__ == "__main__":
with open(sys.argv[1]) as filenamesListFile:
filenames = filenamesListFile.read().split()
with open(sys.argv[2]) as patternsFile:
patterns …Run Code Online (Sandbox Code Playgroud) 在python空闲中:
>>> a=1.1
>>> b=1.1
>>> a is b
False
Run Code Online (Sandbox Code Playgroud)
但是当我将代码放入脚本并运行它时,我会得到不同的结果:
$cat t.py
a=1.1
b=1.1
print a is b
$python t.py
True
Run Code Online (Sandbox Code Playgroud)
为什么会这样?我知道is比较id两个对象,为什么两个对象的id在python脚本/空闲中是相同/唯一的?
我还发现,如果我使用一个小的int,例如1,而不是1.1,结果将在python脚本和python空闲中相同.为什么小型int和小型浮动有不同的行为?
我正在使用CPython 2.7.5.
我有一个带有一些对象的迭代器,我想创建一个uniqueUsers的集合,其中我只列出每个用户一次.所以玩了一下我用列表和字典尝试了它:
>>> for m in ms: print m.to_user # let's first look what's inside ms
...
Pete Kramer
Pete Kramer
Pete Kramer
>>>
>>> uniqueUsers = [] # Create an empty list
>>> for m in ms:
... if m.to_user not in uniqueUsers:
... uniqueUsers.append(m.to_user)
...
>>> uniqueUsers
[Pete Kramer] # This is what I would expect
>>>
>>> uniqueUsers = {} # Now let's create a dict
>>> for m in ms:
... if m.to_user not in uniqueUsers:
... …Run Code Online (Sandbox Code Playgroud) 在Python(2和3)中。每当我们使用列表切片时,它都会返回一个新对象,例如:
l1 = [1,2,3,4]
print(id(l1))
l2 = l1[:]
print(id(l2))
Run Code Online (Sandbox Code Playgroud)
输出量
>>> 140344378384464
>>> 140344378387272
Run Code Online (Sandbox Code Playgroud)
如果使用元组重复相同的事情,则返回相同的对象,例如:
t1 = (1,2,3,4)
t2 = t1[:]
print(id(t1))
print(id(t2))
Run Code Online (Sandbox Code Playgroud)
输出量
>>> 140344379214896
>>> 140344379214896
Run Code Online (Sandbox Code Playgroud)
如果有人能弄清为什么会发生,那将是很棒的,在我的整个Python经验中,我一直以为空切片会返回一个新对象的印象。
我的理解是,它返回的对象与元组是不可变的相同,因此没有必要为其创建新副本。但是同样,文档中也没有提到它。
cpython ×10
python ×10
performance ×3
list ×2
python-2.7 ×2
boolean ×1
c ×1
c++ ×1
dictionary ×1
functools ×1
gcc ×1
if-statement ×1
memory ×1
set ×1
slice ×1
spotify ×1
string ×1
tuples ×1