with tutorial script scraping scrap con code python design-patterns builder-pattern

tutorial - Patrón de generador equivalente en Python



using beautifulsoup python (4)

El patrón de generador en Java se puede lograr fácilmente en python utilizando una variante de:

MyClass(self, required=True, someNumber=<default>, *args, **kwargs)

donde sea required y someNumber son un ejemplo para mostrar los params requeridos con un valor predeterminado y luego leer argumentos variables al manejar el caso donde podría haber None

En caso de que no haya usado argumentos de variables antes, remítase a this

En Java, puede usar el patrón de generador para proporcionar un medio más legible para crear instancias de una clase con muchos parámetros. En el patrón del generador, uno construye un objeto de configuración con métodos para establecer atributos con nombre, y luego lo usa para construir otro objeto.

¿Cuál es el equivalente en Python? ¿Es la mejor manera de imitar la misma implementación?


Los OP se prepararon para una caída proyectando el patrón Builder como específico de Java. No es. Está en el libro de Gang of Four y es potencialmente relevante para cualquier lenguaje orientado a objetos.

Desafortunadamente, incluso el artículo de Wikipedia sobre el patrón de Constructor no le da suficiente crédito. No es simplemente útil para la elegancia del código. Los patrones de compilador son una excelente forma de crear objetos inmutables que deben ser mutables hasta que se utilicen. El estado inmutable es especialmente crítico en los paradigmas funcionales, lo que hace que el Generador sea un excelente patrón orientado a objetos para Python.

He proporcionado un ejemplo de implementación de Builder + ImmutableObject a continuación usando collections.namedtuple , tomado y modificado de " Cómo hacer un objeto inmutable en python ". He mantenido el Constructor bastante simple. Sin embargo, se podrían proporcionar funciones de configuración que devuelvan el Constructor para permitir el encadenamiento de llamadas. O bien, la sintaxis de @property se podría usar en el Generador para proporcionar definidores de atributos que verifiquen la validez de los atributos antes de la configuración.

from collections import namedtuple IMMUTABLE_OBJECT_FIELDS = [''required_function_result'', ''required_parameter'', ''default_parameter''] class ImmutableObjectBuilder(object): def __init__(self, required_function, required_parameter, default_parameter="foo"): self.required_function = required_function self.required_parameter = required_parameter self.default_parameter = default_parameter def build(self): return ImmutableObject(self.required_function(self.required_parameter), self.required_parameter, self.default_parameter) class ImmutableObject(namedtuple(''ImmutableObject'', IMMUTABLE_OBJECT_FIELDS)): __slots__ = () @property def foo_property(self): return self.required_function_result + self.required_parameter def foo_function(self): return self.required_function_result - self.required_parameter def __str__(self): return str(self.__dict__)

Ejemplo de uso:

my_builder = ImmutableObjectBuilder(lambda x: x+1, 2) obj1 = my_builder.build() my_builder.default_parameter = "bar" my_builder.required_parameter = 1 obj2 = my_builder.build() my_builder.required_function = lambda x: x-1 obj3 = my_builder.build() print obj1 # prints "OrderedDict([(''required_function_result'', 3), (''required_parameter'', 2), (''default_parameter'', ''foo'')])" print obj1.required_function_result # prints 3 print obj1.foo_property # prints 5 print obj1.foo_function() # prints 1 print obj2 # prints "OrderedDict([(''required_function_result'', 2), (''required_parameter'', 1), (''default_parameter'', ''bar'')])" print obj3 # prints "OrderedDict([(''required_function_result'', 0), (''required_parameter'', 1), (''default_parameter'', ''bar'')])"

En este ejemplo, creé tres ImmutableObjects, todos con diferentes parámetros. Le he dado a la persona que llama la capacidad de copiar, modificar y pasar una configuración mutable en la forma del constructor mientras se garantiza la inmutabilidad de los objetos construidos. Establecer y eliminar atributos en ImmutableObjects generará errores.

En pocas palabras: los constructores son una excelente manera de pasar algo con estado mutable que proporciona un objeto con un estado inmutable cuando estás listo para usarlo. O, dicho de otra manera, los constructores son una excelente forma de proporcionar los ajustadores de atributos sin dejar de garantizar un estado inmutable. Esto es especialmente valioso en paradigmas funcionales.


Los patrones de diseño a menudo se pueden reemplazar con funciones de lenguaje integradas.

Tu caso de uso

Usted dice "quería tener una versión más legible" y significa "crear instancias de una clase con muchos parámetros". En el caso de Java:

[A] caso de uso para el patrón del constructor es cuando el constructor del objeto que se construirá debe tomar muchos parámetros. En tales casos, a menudo es más conveniente agrupar dichos parámetros de configuración en un objeto constructor ( setMaxTemperature(int t) , setMinTemperature(int t) , set .., etc.) que cargar al llamante con una larga lista de argumentos para pasar el constructor de la clase. .

El patrón del constructor no es necesario

Pero Python admite parámetros con nombre , por lo que esto no es necesario. Puedes simplemente definir el constructor de una clase:

class SomeClass(object): def __init__(self, foo="default foo", bar="default bar", baz="default baz"): # do something

y llamarlo usando parámetros nombrados:

s = SomeClass(bar=1, foo=0)

Tenga en cuenta que puede reordenar y omitir argumentos libremente, al igual que con un generador en Java puede omitir o reordenar llamadas a los métodos set en el objeto del generador.

También vale la pena mencionar que la naturaleza dinámica de Python le da más libertad sobre la construcción de objetos (usando __new__ etc.), que puede reemplazar otros usos del patrón del constructor.

Pero si realmente quieres usarlo

puede usar collections.namedtuple como su objeto de configuración. namedtuple() devuelve un nuevo tipo que representa una tupla, cada uno de cuyos parámetros tiene un nombre de pila, sin tener que escribir una clase repetitiva. Puede usar objetos del tipo resultante de forma similar a los constructores de Java. (Gracias a Paul McGuire por sugerir esto.)

StringBuilder

Un patrón relacionado es el StringBuilder de Java, que se usa para construir eficientemente una String (inmutable) en etapas. En Python, esto se puede reemplazar con str.join . Por ejemplo:

final StringBuilder sb = new StringBuilder(); for(int i = 0; i < 100; i++) sb.append("Hello(" + i + ")"); return sb.toString();

puede ser reemplazado por

return "".join("Hello({})".format(i) for i in range(100))


No estoy de acuerdo con @MechanicalSnail. Creo que una implementación de generador similar a la referenciada por el póster sigue siendo muy útil en algunos casos. Los parámetros nombrados solo le permitirán simplemente establecer las variables miembro. Si quieres hacer algo un poco más complicado, no tienes suerte. En mi ejemplo, uso el patrón de generador clásico para crear una matriz.

class Row_Builder(object): def __init__(self): self.row = ['''' for i in range(170)] def with_fy(self, fiscal_year): self.row[FISCAL_YEAR] = fiscal_year return self def with_id(self, batch_id): self.row[BATCH_ID] = batch_id return self def build(self): return self.row

Utilizándolo:

row_FY13_888 = Row_Builder().with_fy(''FY13'').with_id(''888'').build()