扩展现有Python模块的最佳实践是什么 - 在这种情况下,我想扩展 python-twitter 通过在基本API类中添加新方法包装。

我看了看 tweepy, ,我也喜欢;我只是找到 python-twitter 更容易理解和扩展我想要的功能。

我已经编写了方法 - 我正在尝试找出最柔软,最小破坏性的方法,以将它们添加到 python-twitter 软件包模块,无需更改此模块的核心。

有帮助吗?

解决方案

几种方式。

简单的方法:

不要扩展模块,扩展类。

exttwitter.py

import twitter

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

缺点:Twitter中的每个类都必须在exttwitter.py中,即使只是一个存根(如上)

一种更艰难的(可能是无纹状体)的方式:

从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中的双重导入是否为Pythonic-您可以将其删除,但是您不处理想要扩展基本模块名称空间的函数的用户酶。

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

其他提示

不要将它们添加到模块中。子类要扩展的类并在自己的模块中使用子类,而根本不更改原始内容。

这是您可以在运行时直接操纵模块列表的方式 - 扰流板警报: 您从中获得模块类型 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 模块 - 您总是可以做类似的事情 ModuleType = type(os) 导入后 os - 我特别指出了这种类型的来源,因为它不是很明显的;与其他许多内置类型不同,Python无法提供对全局名称空间中模块类型的访问。)

真正的动作是 sys.modules dict,在哪里(如果您非常勇敢)可以替换现有模块并添加新模块。

说您有一个较旧的模块称为 mod 您这样使用:

import mod

obj = mod.Object()
obj.method()
mod.function()
# and so on...

您想扩展它,而无需为用户替换它。容易完成。您可以给您的新模块一个不同的名称, newmod.py 或以同一名称将其放在更深的道路上并保持相同的名字,例如 /path/to/mod.py. 。然后,您的用户可以以这些方式导入它:

import newmod as mod       # e.g. import unittest2 as unittest idiom from Python 2.6

或者

from path.to import mod    # useful in a large code-base

在您的模块中,您需要使所有可用的旧名称:

from mod import *

或明确命名您导入的每个名称:

from mod import Object, function, name2, name3, name4, name5, name6, name7, name8, name9, name10, name11, name12, name13, name14, name15, name16, name17, name18, name19, name20, name21, name22, name23, name24, name25, name26, name27, name28, name29, name30, name31, name32, name33, name34, name35, name36, name37, name38, name39

我觉得 import * 对于此用例,将更加可维护 - 如果基本模块扩展功能,您将无缝地跟上(尽管您可能会以相同名称来遮挡新对象)。

如果是 mod 您正在扩展有一个不错的 __all__, ,它将限制导入的名称。

您还应该声明 __all__ 并使用扩展模块扩展 __all__.

import mod
__all__ = ['NewObject', 'newfunction']
__all__ += mod.__all__   
# if it doesn't have an __all__, maybe it's not good enough to extend
# but it could be relying on the convention of import * not importing
# names prefixed with underscores, (_like _this)

然后像往常一样扩展对象和功能。

class NewObject(object):
    def newmethod(self):
        """this method extends Object"""

def newfunction():
    """this function builds on mod's functionality"""

如果新对象提供您打算替换的功能(或者您将新功能退回到较旧的代码库中),则可以覆盖名称

我可以建议不要在这里重新发明轮子吗?我正在建立一个> 6K的Twitter客户端,现在为2个月,首先我也检查了Python-twitter,但是它落后于最近的API更改,开发似乎也不是那么活跃,也有(至少在上次检查时)不支持OAuth/Xauth)。

因此,在搜索更多之后,我发现了Tweepy:
http://github.com/joshthecoder/tweepy

优点:主动开发,OAAUTH/XAUTH,并与API保持最新。
您需要的东西已经很高。

所以我建议这样做,这对我有用,我唯一要添加的是Xauth(Xauth(它可以合并回tweepy :)

哦,一个无耻的插头,如果您需要解析推文和/或格式化html,请使用我的python版本的twitter-text-*库:
http://github.com/bonsaiden/twitter-text-python

就像Twitter.com一样,这件事是UnitTestetd保证可以解析推文。

定义一个新类,而不是从要从原始模块扩展的类中继承它,而是将原始类的实例作为属性添加到新类中。这是一个技巧:拦截所有不存在的方法在您的新课程上调用,并尝试在旧类的实例上调用它。在您的新频道中,只需按照您的要求定义新的或覆盖的方法:

import originalmodule

class NewClass:
    def __init__(self, *args, **kwargs):
        self.old_class_instance = originalmodule.create_oldclass_instance(*args, **kwargs)

    def __getattr__(self, methodname):
        """This is a wrapper for the original OldClass class.

        If the called method is not part of this NewClass class,
        the call will be intercepted and replaced by the method
        in the original OldClass instance.
        """
        def wrapper(*args, **kwargs):
            return getattr(self.old_class_instance, methodname)(*args, **kwargs)
        return wrapper

    def new_method(self, arg1):
        """Does stuff with the OldClass instance"""
        thing = self.old_class_instance.get_somelist(arg1)
        # returns the first element only
        return thing[0]

    def overridden_method(self):
        """Overrides an existing method, if OldClass has a method with the same name"""
        print("This message is coming from the NewClass and not from the OldClass")

就我而言,当无法从旧类中简单继承时,我使用了该解决方案,因为必须由其构造函数创建一个实例,而是由其他类/模块的初始脚本创建的。 (这是上面示例中的OriginalModule.Create_oldClass_instance。)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top