Как расширить модуль Python?Добавление новой функциональности в пакет `python-twitter`

StackOverflow https://stackoverflow.com/questions/2705964

Вопрос

Как лучше всего расширить существующий модуль Python? В данном случае я хочу расширить python-twitter пакет, добавив новые методы в базовый класс API.

я посмотрел tweepy, и мне это тоже нравится;я просто нахожу python-twitter легче понять и расширить за счет нужной мне функциональности.

У меня уже написаны методы — я пытаюсь найти наиболее Pythonic и наименее разрушительный способ добавить их в python-twitter package-модуль, не меняя ядра этого модуля.

Это было полезно?

Решение

Несколько способов.

Простой способ:

Не расширяйте модуль, расширяйте классы.

exttwitter.py

import twitter

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

Недостаток:Каждый класс в Твиттере должен находиться в 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 является питоническим — вы можете удалить его, но тогда вы не справитесь с вариантом использования, когда требуется расширить функцию, которая находилась в пространстве имен базового модуля.

Что касается расширенных классов, просто создайте новый класс 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"""

Если новые объекты предоставляют функциональность, которую вы собираетесь заменить (или, возможно, вы переносите новую функциональность в более старую базу кода), вы можете перезаписать имена.

Могу ли я предложить здесь не изобретать колесо?Я создаю клиент Twitter> 6 тыс. строк уже 2 месяца, сначала я тоже проверил python-twitter, но он сильно отстает от недавних изменений API. Разработка, похоже, тоже не такая активная, также были (по крайней мере, когда я последний раз проверял) нет поддержки OAuth/xAuth).

Итак, покопавшись еще немного, я обнаружил чириканье:
http://github.com/joshthecoder/tweepy

Плюсы:Активная разработка, OAauth/xAuth и обновленный API.
Велика вероятность, что то, что вам нужно, уже есть.

Поэтому я предлагаю пойти по этому пути, у меня это работает, единственное, что мне нужно было добавить, это xAuth (который снова слился с tweepy :)

Ох, бесстыдная затычка! Если вам нужно проанализировать твиты и/или отформатировать их в HTML, используйте мою версию библиотек twitter-text-* на Python:
http://github.com/BonsaiDen/twitter-text-python

Эта штука гарантированно анализирует твиты точно так же, как это делает Twitter.com.

Определите новый класс и вместо того, чтобы наследовать его от класса, который вы хотите расширить из исходного модуля, добавьте экземпляр исходного класса в качестве атрибута к вашему новому классу.И вот тут подвох:перехватить все несуществующие вызовы методов вашего нового класса и попытаться вызвать его на экземпляре старого класса.В вашем 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.)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top