Question

Quelles sont les meilleures pratiques pour l'extension d'un module Python existant -. Dans ce cas, je veux étendre le paquet à la python-twitter en ajoutant de nouvelles méthodes pour la classe API de base

Je l'ai regardé tweepy, et je comme ça aussi; Je trouve python-twitter plus facile à comprendre et à étendre la fonctionnalité que je veux.

Je les méthodes déjà écrit - je suis en train de comprendre le plus Pythonic et le moins perturbateur pour les ajouter dans le module de package python-twitter, sans changer le noyau de ces modules

.
Était-ce utile?

La solution

Quelques façons.

La façon simple:

Ne pas prolonger le module, étendre les classes.

exttwitter.py

import twitter

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

Inconvénient: Chaque classe doit être twitter exttwitter.py, même si elle est juste un talon (comme ci-dessus)

Une façon plus dur (peut-être un-pythonique):

Importer * de python-twitter dans un module que vous Prolongez ensuite.

Par exemple:

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

Je ne sais pas si la double importation extmodule.py est pythonique - vous pouvez le supprimer, mais vous ne gère pas le usecase de vouloir étendre une fonction qui était dans l'espace de noms basemodule.

En ce qui concerne les classes étendues, il suffit de créer une nouvelle classe API (basemodule.API) pour étendre le module API Twitter.

Autres conseils

Ne pas les ajouter au module. Les classes que vous sous-classe souhaitez étendre et utiliser vos sous-classes dans votre propre module, ne change pas la substance originale du tout.

Voici comment vous pouvez manipuler directement la liste des modules lors de l'exécution - Alerte spoiler: vous obtenez le type de module de module 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

... vous pouvez ensuite utiliser cette fonction comme ceci:

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__}")

... Vous pouvez aussi créer votre propre sous-classe de module, en spécifiant types.ModuleType comme l'ancêtre de votre class nouvellement déclarée, bien sûr; Je ne l'ai jamais personnellement trouvé cela nécessaire de le faire.

(En outre, vous ne le faites pas Vous pour obtenir le type de module à partir du module types - vous pouvez toujours faire quelque chose comme ModuleType = type(os) après l'importation os - Je spécifiquement souligné cette source du type car il est non évidente,. contrairement à beaucoup de ses autres types de BUILTIN, Python ne propose pas accès au type de module dans l'espace de noms global)

L'action réelle est dans le dict sys.modules, où (si vous êtes correctement intrépide), vous pouvez remplacer les modules existants ainsi que l'ajout de vos nouvelles.

Disons que vous avez un ancien module appelé mod que vous utilisez comme ceci:

import mod

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

Et vous voulez prolonger, sans le remplacer pour vos utilisateurs. Facile à faire. Vous pouvez donner à votre nouveau module un nom différent, newmod.py ou placez-le par même nom à un chemin plus profond et conserver le même nom, par exemple /path/to/mod.py. Ensuite, vos utilisateurs peuvent importer dans l'une des façons suivantes:

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

ou

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

Dans votre module, vous aurez envie de faire tous les anciens noms disponibles:

from mod import *

ou explicitement le nom de tous les noms que vous importez:

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

Je pense que le import * sera plus maintenable pour ce cas d'utilisation - si le module de base augmente la fonctionnalité, vous restez en toute transparence (si vous pouvez l'ombre de nouveaux objets avec le même nom).

Si le mod vous étendez a un __all__ décent, il limite les noms importés.

Vous devriez également déclarer un __all__ et l'étendre avec __all__ du module étendu.

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)

étendre ensuite les objets et fonctionnalités que vous le feriez normalement.

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

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

Si les nouveaux objets offrent des fonctionnalités que vous souhaitez remplacer (ou peut-être vous rétroportage la nouvelle fonctionnalité dans une base de code plus), vous pouvez remplacer les noms

Je vous suggère de ne pas réinventer la roue? Je construis une> 6k ligne Twitter Client pour 2 mois maintenant, tout d'abord j'ai vérifié python-twitter aussi, mais il est en retard beaucoup derrière les récents changements de l'API ,, le développement ne semble pas être actif que ce soit, il y avait aussi (au moins quand je suis arrivé dernier) pas de support pour OAuth / xAUTH).

Ainsi, après la recherche autour d'un peu plus je découvre tweepy:
http://github.com/joshthecoder/tweepy

Plus:. Développement actif, OAauth / XAUTH et à jour avec l'API
Les chances sont élevées que ce que vous avez besoin est déjà là.

Alors je suggère d'aller avec cela, il travaille pour moi, la seule chose que je devais ajouter était XAUTH (ce retour de fusion obtenu à tweepy:)

Oh un un bouchon sans vergogne, si vous avez besoin d'analyser Tweets et / ou les formater en HTML utiliser ma version python des * twitter-texte- bibliothèques:
http://github.com/BonsaiDen/twitter-text-python

Cette chose est unittestetd une garantie à Tweets parse tout comme Twitter.com fait.

Définir une nouvelle classe, et au lieu de posséderai de la classe que vous souhaitez étendre à partir du module original, ajoutez une instance de la classe d'origine comme un attribut à votre nouvelle classe. Et voici l'astuce: intercepter tous les appels de méthode non-existant sur votre nouvelle classe et essayer de l'appeler sur l'instance de l'ancienne classe. Dans votre NewClass définir simplement des méthodes nouvelles ou redéfinies comme vous aimez:

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")

Dans mon cas, j'utilisé cette solution lorsque l'héritage simple à partir de l'ancienne classe n'a pas été possible, car une instance devait être créé non pas par son constructeur, mais avec un script d'initialisation à partir d'une autre classe / module. (Il est le originalmodule.create_oldclass_instance dans l'exemple ci-dessus.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top