Pregunta

¿Cuáles son las mejores prácticas para ampliar un módulo de Python existente -. En este caso, quiero extender el paquete python-twitter mediante la adición de nuevos métodos para la clase API

He mirado en tweepy, y eso me gusta, así; Acabo de encontrar python-twitter más fácil de entender y extender con la funcionalidad que quiero.

Tengo los métodos ya escritos - Estoy tratando de averiguar el más Pythonic y forma menos perjudicial para agregarlos en el módulo de paquete python-twitter, sin cambiar núcleo estos módulos

.
¿Fue útil?

Solución

Algunas maneras.

La forma más fácil:

No extienda el módulo, se extienden las clases.

exttwitter.py

import twitter

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

Lo malo: Cada clase en Twitter debe estar en exttwitter.py, incluso si es sólo un talón (como el anterior)

A más difícil manera (posiblemente un-Pythonic):

Importar * desde pitón-twitter en un módulo que se extienden entonces.

Por ejemplo:

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

No estoy seguro de si el doble de importaciones en extmodule.py es Pythonic - podría eliminarlo, pero entonces no manejan el caso de uso de querer extender una función que estaba en el espacio de nombres de basemodule.

En cuanto a las clases extendidas, solo crea una nueva API de la clase (basemodule.API) para extender el módulo API de Twitter.

Otros consejos

No añadir al módulo. Subclase las clases que desea ampliar y utilizar sus subclases en su propio módulo, sin cambiar el material original en absoluto.

Así es como se puede manipular directamente la lista de módulos en tiempo de ejecución - alerta de spoiler: que presentamos lo mejor el tipo de módulo del módulo 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

... A continuación, puede utilizar una función de este modo:

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

... Se podría también crear su propia subclase módulo, especificando types.ModuleType como el antepasado de su class recién declarado, por supuesto; Nunca he encontrado personalmente este necesario hacerlo.

(También, no lo hace Tienes para obtener el tipo de módulo del módulo types - siempre se puede simplemente hacer algo como ModuleType = type(os) después de importar os - me señaló específicamente la fuente única del tipo porque no es obvia;. a diferencia de muchos de sus otros tipos internos, Python no ofrece el acceso al tipo de módulo en el espacio de nombres global)

La verdadera acción está en el dict sys.modules, donde (si está adecuadamente intrépido) puede reemplazar módulos existentes, así como la adición de los nuevos.

Supongamos que tiene un módulo más antiguo llamado mod que utilice la siguiente manera:

import mod

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

Y desea extenderlo, sin reemplazarlo para sus usuarios. Fácil de hacer. Usted puede dar a su nuevo módulo de un nombre diferente, newmod.py o colocarlo por mismo nombre en un camino más profundo y mantener el mismo nombre, por ejemplo, /path/to/mod.py. A continuación, los usuarios pueden importar en cualquiera de estas maneras:

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

o

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

En su módulo, tendrá que hacer todos los viejos nombres disponibles:

from mod import *

o nombrar explícitamente todos los nombres de importar:

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

Creo que el import * será más fácil de mantener para este caso de uso - si el módulo base se amplía la funcionalidad, se le mantienen al día sin problemas (aunque es posible que la sombra nuevos objetos con el mismo nombre).

Si el mod está extendiendo tiene una __all__ decente, restringirá los nombres importados.

También debe declarar un __all__ y ampliarlo con __all__ del módulo extendido.

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)

A continuación, extender los objetos y funcionalidad como lo haría normalmente.

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

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

Si los nuevos objetos proporcionan funcionalidad tiene la intención de reemplazar (o tal vez usted está backporting la nueva funcionalidad en una base de código antiguo) puede sobrescribir los nombres

¿Puedo sugerir que no reinventar la rueda? Estoy construyendo una> 6k línea de cliente de Twitter hace 2 mes, en un primer momento he comprobado python-twitter también, pero está quedando mucho detrás de los recientes cambios en la API de Desarrollo ,, no parece ser que sea activa, también hubo (al menos la última vez que revisé) no hay soporte para OAuth / XAUTH).

Así que después de buscar en torno a un poco más descubrí tweepy:
http://github.com/joshthecoder/tweepy

Pros:. El desarrollo activo, OAauth / XAUTH y hasta la fecha con la API
Hay muchas posibilidades de que lo que necesita ya está ahí.

Así que le sugiero ir con eso, está funcionando para mí, lo único que tenía que añadir era XAUTH (que volver fusión tiene que tweepy:)

Oh un tapón de una desvergonzada, si es necesario analizar los Tweets y / o darles formato HTML a utilizar mi versión pitón de las bibliotecas * twitter-texto-:
http://github.com/BonsaiDen/twitter-text-python

Esta cosa es un unittestetd garantizado a los Tweets de análisis sintáctico al igual que lo hace Twitter.com.

Definir una nueva clase, y en vez de heredan de la clase que desea que se extienda desde el módulo original, añadir una instancia de la clase original como un atributo a su nueva clase. Y aquí viene el truco: intercepción de todas las llamadas inexistentes método en su nueva clase y tratar de llamarlo en la instancia de la clase de edad. En su NewClass acaba de definir los métodos nuevos o cambiados a su gusto:

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

En mi caso he utilizado esta solución cuando la herencia sencilla de la clase de edad no fue posible, debido a una instancia tuvo que ser creada no por su constructor, pero con un guión de inicio de una otra clase / módulo. (Es la originalmodule.create_oldclass_instance en el ejemplo anterior.)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top