扩展现有Python模块的最佳实践是什么 - 在这种情况下,我想通过向基本API类添加新方法来扩展python-twitter
包。
我看过tweepy
,我也喜欢这样;我发现python-twitter
更容易理解并扩展我想要的功能。
我已经编写了方法 - 我试图找出将Pythonic和破坏性最小的方法添加到python-twitter
包模块中,而不更改此模块的核心。
答案 0 :(得分:21)
有几种方法。
简单方法:
不要扩展模块,扩展类。
exttwitter.py
import twitter
class Api(twitter.Api):
pass
# override/add any functions here.
下行:twitter中的每个类都必须在exttwitter.py中,即使它只是一个存根(如上所述)
更难(可能是非pythonic)的方式:
将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 - 你可以删除它,但是你不会处理想要扩展basemodule命名空间中的函数的用例。
就扩展类而言,只需创建一个新的API(basemodule.API)类来扩展Twitter API模块。
答案 1 :(得分:5)
不要将它们添加到模块中。对要扩展的类进行子类化,并在您自己的模块中使用子类,而不是更改原始内容。
答案 2 :(得分:4)
在这里,您可以在运行时直接操作模块列表– 剧透警报:您可以从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
模块中获取模块类型-您始终可以在导入{{1}之后执行ModuleType = type(os)
之类的操作} –我特别指出了这种类型的一种来源,因为它不是显而易见的;与它的许多其他内置类型不同,Python不提供对全局命名空间中模块类型的访问。)
真正的动作是在os
字典中,在这里(如果您非常勇敢)可以替换现有模块,也可以添加新模块。
答案 3 :(得分:3)
假设您有一个名为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"""
如果新对象提供您要替换的功能(或者您可能将新功能向后移植到较旧的代码库中),则可以覆盖名称
答案 4 :(得分:2)
我建议不要在这里重新发明轮子吗?我现在正在建立一个> 6k线的Twitter客户端2个月,起初我也检查了python-twitter,但是它落后于最近的API变化,发展似乎也没那么活跃,也有是(至少在我上次检查时)不支持OAuth / xAuth。
所以在搜索了一下之后我发现了tweepy:
http://github.com/joshthecoder/tweepy
优点:积极开发,OAauth / xAuth以及API的最新版本 你需要的东西已经很高了。
所以我建议继续使用它,它对我有用,我唯一需要添加的是xAuth(将其合并回到tweepy:)
哦,这是一个无耻的插件,如果您需要解析推文和/或将它们格式化为HTML,请使用我的python版本的twitter-text- *库:
http://github.com/BonsaiDen/twitter-text-python
这件事是unittestetd保证解析推文就像Twitter.com那样。
答案 5 :(得分:0)
定义一个新类,而不是从要从原始模块扩展的类中继承它,而是将原始类的实例作为属性添加到新类中。 诀窍就在这里:拦截新类上所有不存在的方法调用,然后尝试在旧类的实例上调用它。 在您的NewClass中,根据需要定义新方法或覆盖方法:
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。)