I have looked at What is a clean pythonic way to have multiple constructors in python, but I need further assistance since I'm still a newbie. The example there is when you have just one parameter, and I have four.
Let's say that I have a class:
class Word:
def __init__(self, wordorphrase, explanation, translation, example):
self.wordorphrase = wordorphrase
self.explanation = explanation
self.example = example
self.translation = translation
Now I can create Word objects only by passing four parameters when creating an object, for instance:
w = Word(self.get_word(), self.get_explanation(), self.get_translation(), self.get_example())
How should I modify my __init__ method so that I can create objects by:
w = Word()
One way that you could accomplish this is by specifying default arguments for your four variables that are not self. I tested the following code and was able to create instances of the word class without passing any arguments.
class Word:
def __init__(self, wordorphrase=None, explanation=None, translation=None, example=None):
self.wordorphrase = wordorphrase
self.explanation = explanation
self.example = example
self.translation = translation
Related
Context:
I've got this composition (new vocabulary word for me) of an OneHotEncoder object:
class CharEncoder:
characters = cn.ALL_LETTERS_ARRAY
def __init__(self):
self.encoder = OneHotEncoder(sparse=False).fit(self.characters.reshape(-1, 1))
self.categories = self.encoder.categories_[0].tolist()
def transform(self, word):
word = np.array(list(word)).reshape(-1, 1)
word_vect = self.encoder.transform(word)
return word_vect
def inverse_transform(self, X):
word_arr = self.encoder.inverse_transform(X).reshape(-1,)
return ''.join(word_arr)
As you can see it has a class attribute characters which is essentially an array of all the ASCII characters plus some punctuation.
I want to make this CharEncoder class useful for more than just ASCII. Maybe someone else would really like to use a different character set, and I want to allow them to do so. Or maybe they want to encode entire words instead of individual letters... who knows!?
My problem:
I feel like there are so many design choices here that could make this code re-usable for a slightly different task. I feel overwhelmed.
Do I make the character set a class attribute or an instance attribute?
Do I write getters and setters for the character set?
Do I instead write some parent class, and sub-classes for different character sets.
Or do I make users pass their own OneHotEncoder object to my class, and not worry about it myself?
My question:
What are some considerations that might help guide my design choice here?
I'd just make characters an instance attribute with a default value.
class CharEncoder:
def __init__(self, characters=cn.ALL_LETTERS_ARRAY):
self.characters = characters
self.encoder = OneHotEncoder(sparse=False).fit(self.characters.reshape(-1, 1))
self.categories = self.encoder.categories_[0].tolist()
Caution: If cn.ALL_LETTERS_ARRAY is mutable (ie a Python list or a numpy array), use None as a sentinel value:
def __init__(self, characters=None):
self.characters = characters or cn.ALL_LETTERS_ARRAY
# a shorter version for
# if characters is None:
# self.characters = cn.ALL_LETTERS_ARRAY
# else:
# self.characters = characters
# with a small caveat that self.characters can't be set to
# an empty string/list/array/dict because these evaluate to False
Usage:
default_chars_encoder = CharEncoder() # using the default cn.ALL_LETTERS_ARRAY
custom_chars_encoder = CharEncoder(CUSTOM_CHARCTERS_SET) # using CUSTOM_CHARCTERS_SET
I have defined a of ordered pairs called f and defined a function applyfunction that goes through the ordered pairs looking at the first value to compare and when it does match to print the second value.
f = {(1,2),(2,4),(3,6),(4,8)}
def applyfunction (f,x):
for xy in f:
if xy[0]==x:
print(xy[1])
applyfunction(f,3)
The above works just the way I want it to. In the meantime I have seen that in python there are functions that have a dot notation and I think that would be useful here. So my question, how can I rewrite the applyfunction definition such that I can use the following notation: f.applyfunction(3)?
You can wrap the ordered pairs into a class of your own, which has the method (method == a function inside a class) you mentioned inside of it.
class OrderedPairWrapper():
def __init__(self, op):
self.op = op
def applyfunction (self, x):
for xy in self.op:
if xy[0]==x:
print(xy[1])
f = {(1,2),(2,4),(3,6),(4,8)}
f = OrderedPairWrapper(f)
print(f.applyfunction(3))
# 6
Dots are used to access methods of a class using its object name. If you want to access that using dot operator, create an object called f for a class with a method applyfunction. Then you can accomplish your desired task
Below is a pattern from :https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Factory.html . My question is this, is this still the best idiom/pattern to do generic object creation in Python 3.x? I can't seem to find much on this topic. The code is below:
class Shape(object):
# Create based on class name:
def factory(type):
#return eval(type + "()")
if type == "Circle": return Circle()
if type == "Square": return Square()
assert 0, "Bad shape creation: " + type
factory = staticmethod(factory)
class Circle(Shape):
def draw(self): print("Circle.draw")
def erase(self): print("Circle.erase")
class Square(Shape):
def draw(self): print("Square.draw")
def erase(self): print("Square.erase")
# Generate shape name strings:
def shapeNameGen(n):
types = Shape.__subclasses__()
for i in range(n):
yield random.choice(types).__name__
shapes = \
[ Shape.factory(i) for i in shapeNameGen(7)]
for shape in shapes:
shape.draw()
shape.erase()
You can also create a factory by using the __class__ method as well I've noticed, but I'm unsure of the best way to use this.
I could be missing something, but I don't like this pattern.
You already have factories for Circle and Square - Circle and Square. :)
The code in your question unnecessarily hardcodes the class names in factory and then goes through some extra hoops by getting the names of the subclasses of Shape and then calling factory with those names.
A more direct way to generate the shapes list is
types = Shape.__subclasses__()
shapes = [random.choice(types)() for _ in range(7)]
I have a situation where I could have multiple geometries being given. This is an over simplified example, but I am getting JSON response of various geometries, and instead of having to write multiple if statements multiple times, I thought using a Factory could reduce the issue: so Shape(JSON) -> Circle or Shape(JSON) - Square
This does not justify the factory as it is coded here. You could have a simple dictionary like
classes = {'Circle': Circle, 'Square': Square, ...}
or possibly create it dynamically with
classes = {cls.__name__:cls for cls in Shape.__subclasses__()}
and then call classes[some_string]() for the instantiation. You can even dynamically instantiate a class by string name using getattr.
I'm attempting to write a function that creates a new subclass named with the string it gets passed as an argument. I don't know what tools would be best for this, but I gave it a shot in the code below and only managed to make a subclass named "x", instead of "MySubClass" as intended. How can I write this function correctly?
class MySuperClass:
def __init__(self,attribute1):
self.attribute1 = attribute1
def makeNewClass(x):
class x(MySuperClass):
def __init__(self,attribute1,attribute2):
self.attribute2 = attribute2
x = "MySubClass"
makeNewClass(x)
myInstance = MySubClass(1,2)
The safest and easiest way to do this would be to use the type builtin function. This takes an optional second argument (tuple of base classes), and third argument (dict of functions). My recommendation would be the following:
def makeNewClass(x):
def init(self,attribute1,attribute2):
# make sure you call the base class constructor here
self.attribute2 = attribute2
# make a new type and return it
return type(x, (MySuperClass,), {'__init__': init})
x = "MySubClass"
MySubClass = makeNewClass(x)
You will need to populate the third argument's dict with everything you want the new class to have. It's very likely that you are generating classes and will want to push them back into a list, where the names won't actually matter. I don't know your use case though.
Alternatively you could access globals and put the new class into that instead. This is a really strangely dynamic way to generate classes, but is the best way I can think of to get exactly what you seem to want.
def makeNewClass(x):
def init(self,attribute1,attribute2):
# make sure you call the base class constructor here
self.attribute2 = attribute2
globals()[x] = type(x, (MySuperClass,), {'__init__': init})
Ryan's answer is complete, but I think it's worth noting that there is at least one other nefarious way to do this besides using built-in type and exec/eval or whatever:
class X:
attr1 = 'some attribute'
def __init__(self):
print 'within constructor'
def another_method(self):
print 'hey, im another method'
# black magics
X.__name__ = 'Y'
locals()['Y'] = X
del X
# using our class
y = locals()['Y']()
print y.attr1
y.another_method()
Note that I only used strings when creating class Y and when initializing an instance of Y, so this method is fully dynamic.
I've looked for quite a while but couldn't find a proper answer to my question:
I have a class containing methods which operate on arrays and I want dynamically create methods with a similar name in another class with a modified output.
I've got something like this so far, can anyone guide me ?
Thanks
Class A():
def__init__(self,array):
self.data = array
def method1(self,*args):
newarray = whatever(self.data,*args)
return newarray
def method2(self,*args):
newarray = whatever2(self.data,*args)
return newarray
I want to be able to use those methods to generate new ones in a more complex class, say:
class B(C): #inherits from C
def __init__(self,[arg1,array]):
#initialize from parent class
C.__init__(self,[arg1,array])
#create new methods for this class using same name
methodnames = [element for element in dir(A) if element[0] != '_']
for methodname in methodnames:
##following works but this is not the output I want here
#self.__dict__[methodname] = getattr(A(array),methodname)
#following doesn't work... at least not as I expect it to
#case1
#self.__dict__[methodname] = [arg1,getattr(A(array),methodname)]
#case2
self.__dict__[methodname] = list([arg1,getattr(A(array),methodname)])
a = array
#following returns a list of [arg1, method] but what I really want is [arg1,newarray]
C([arg1,array]).method1(*args)
OK, so let's try to be clearer:
Class A contains filters, takes an array and applies filter as method, returns filtered data.
Class filters()
def__init__(self,array):
self.data = array
def filter1(self,*args):
newarray = median(self.data,*args)
return newarray
def filter2(self,*args):
newarray = gaussian(self.data,*args)
return newarray
...
In another module, I have class SpecialData, which operates on a list of x,y data (where x and y are iterables, i.e. lists, arrays...). So something like
Class SpecialData():
def __init__(self,[x,y]):
self.data = [x,y]
def power(self,power):
ypow = self.data[1]**power
return [x,pow]
def porod(self):
return [x**4,x**4*y]
....
Now, what I want is to add the filter methods contained in class filters to class SpecialData.
I could, of course do this by re-coding all filters with proper format for SpecialClass. but what I really want, is that each time a new filter is added to class filters, to make it available at runtime in class SpecialData without having to re-hard code the new filter.
So, not being very clever, I tried to read the list of available filters in class filters by:
import filters
filternames = [element for element in dir(filters) if element[0] != '_']
for fitlername in filternames:
generate_filters_in_class_SpecialClass
How do I do this properly ?
I found a number of posts related to this, some using super(), others using SpecialData.dict or even setattr. Since the 2nd seemed more understandable to me, I focused on this one and came up with:
import filters
Class SpecialData():
def __init__(self,[x,y]):
self.data = [x,y]
filternames = [element for element in dir(filters) if element[0] != '_']
for fitlername in filternames:
self.__dict__[fitlername ] = [self.data[0],getattr(filters(self.data[1]),fitlername)]
Of course, this doesn't work, because the list is not callable. If I change the last line to :
self.dict[fitlername ] = list([self.data[0],getattr(filters(self.data[1]),fitlername)])
it returns the method as the 2nd element, rather than the result.
Note that the following works, but this is not what I want...
self.dict[fitlername ] = getattr(filters(self.data[1]),fitlername)
Hope this is clearer now...
I think you are trying to make an advanced use of Python without using/knowing its advanced features, like you are borrowing techniques from another language.
This is not a criticism, but you should have a look on Python tutorial, Python introspection or metaclasses.
I think that if you just complete your knowledge on Python functions you will be easily able to solve your problem in a much simpler way.
Rather than generating a proposed solution, you should make it clearer what you are trying to achieve. Class A is a clear example of the starting point; please post an example of your desired ending point, e.g.
Class B():
def__init__(self,array):
self.data = array
def method1(self,*args):
newarray = ComplexWhatever(self.data,*args)
return newarray
def method2(self,*args):
newarray = EvenBiggerWhatever2(self.data,*args)
return newarray
a = A(input_array)
b = B(input_array)
print(a.method1(args))
print(b.method1(args))
What isn't clear is how you want to "dynamically generate" the new function "ComplexWhatever()" instead of writing the function by hand.