如何扩展Python模块?为`python-twitter`包添加新功能

34 投票
6 回答
39799 浏览
提问于 2025-04-15 21:59

如何扩展一个已有的Python模块是个好问题。在这个例子中,我想通过给基础API类添加新方法来扩展python-twitter这个包。

我看过tweepy,也觉得它不错;不过我觉得python-twitter更容易理解,而且更方便我添加我想要的功能。

我已经写好了这些方法,现在我在想,怎样才能以最符合Python风格、最不影响原有模块的方式,把它们加入到python-twitter包里,而不改变这个模块的核心部分。

6 个回答

6

这里教你怎么在运行时直接操作模块列表——小提示:你可以从types模块获取模块类型:

from __future__ import print_function
import sys
import types
import typing as tx

def modulize(namespace: tx.Dict[str, tx.Any],
             modulename: str,
             moduledocs: tx.Optional[str] = None) -> types.ModuleType:

    """ Convert a dictionary mapping into a legit Python module """

    # Create a new module with a trivially namespaced name:
    namespacedname: str = f'__dynamic_modules__.{modulename}'
    module = types.ModuleType(namespacedname, moduledocs)
    module.__dict__.update(namespace)

    # Inspect the new module:
    name: str = module.__name__
    doc: tx.Optional[str] = module.__doc__
    contents: str = ", ".join(sorted(module.__dict__.keys()))
    print(f"Module name:      {name}")
    print(f"Module contents:  {contents}")
    if doc:
        print(f"Module docstring: {doc}")

    # Add to sys.modules, as per import machinery:
    sys.modules.update({ modulename : module })

    # Return the new module instance:
    return module

… 然后你可以这样使用这个函数:

ns = {
         'func' : lambda: print("Yo Dogg"), # these can also be normal non-lambda funcs
    'otherfunc' : lambda string=None: print(string or 'no dogg.'),
      '__all__' : ('func', 'otherfunc'),
      '__dir__' : lambda: ['func', 'otherfunc'] # usually this’d reference __all__
}

modulize(ns, 'wat', "WHAT THE HELL PEOPLE")
import wat

# Call module functions:
wat.func()
wat.otherfunc("Oh, Dogg!")

# Inspect module:
contents = ", ".join(sorted(wat.__dict__.keys()))
print(f"Imported module name:      {wat.__name__}")
print(f"Imported module contents:  {contents}")
print(f"Imported module docstring: {wat.__doc__}")

… 你也可以创建自己的模块子类,只需把types.ModuleType作为你新定义的class的父类;不过我个人觉得这样做并不是必须的。

(另外,你并不一定非得从types模块获取模块类型——你也可以在导入os后直接写ModuleType = type(os)。我特别提到这个来源是因为它不太明显;与许多其他内置类型不同,Python并没有在全局命名空间中直接提供模块类型的访问。)

真正的操作是在sys.modules这个字典里,如果你足够大胆的话,你可以替换已有的模块,也可以添加你新的模块。

7

不要把它们直接加到模块里。你可以创建一个新的类,继承你想要扩展的类,然后在你自己的模块里使用这些新创建的类,这样就不会改变原来的东西。

36

有几种方法。

简单的方法:

不要扩展模块,扩展类。

exttwitter.py

import twitter

class Api(twitter.Api):
    pass 
    # override/add any functions here.

缺点:twitter中的每个类都必须放在exttwitter.py里,即使它只是个空壳(像上面那样)

更复杂(可能不太符合Python风格)的方法:

从python-twitter导入所有内容到一个你要扩展的模块里。

比如:

basemodule.py

 class Ball():
    def __init__(self,a):
        self.a=a
    def __repr__(self):
        return "Ball(%s)" % self.a

def makeBall(a):
    return Ball(a)

def override():
    print "OVERRIDE ONE"

def dontoverride():
    print "THIS WILL BE PRESERVED"

extmodule.py

from basemodule import *
import basemodule

def makeBalls(a,b):
    foo = makeBall(a)
    bar = makeBall(b)
    print foo,bar

def override():
    print "OVERRIDE TWO"

def dontoverride():
    basemodule.dontoverride()
    print "THIS WAS PRESERVED"

runscript.py

import extmodule

#code is in extended module
print extmodule.makeBalls(1,2)
#returns Ball(1) Ball(2)

#code is in base module
print extmodule.makeBall(1)
#returns Ball(1)

#function from extended module overwrites base module
extmodule.override()
#returns OVERRIDE TWO

#function from extended module calls base module first
extmodule.dontoverride()
#returns THIS WILL BE PRESERVED\nTHIS WAS PRESERVED

我不确定在extmodule.py中双重导入是否符合Python风格——你可以去掉一个,但那样就无法处理想要扩展basemodule命名空间中某个函数的情况。

至于扩展类,只需创建一个新的API(basemodule.API)类来扩展Twitter API模块。

撰写回答