I'm trying to initialize a superclass before calling the subclass. Here is an example of what I'm trying to do:
class AbstractSourceParser(object):
def __init__(self, filepath):
self.source_type = get_type_from_filepath(filepath)
#staticmethod
def factory(source_type):
source_type = source_type.upper() if source_type else None
if source_type == SourceType.CSV.upper():
return CSVSourceParser()
class CSVSourceParser(AbstractSourceParser):
...
And then I want to be able to call it like this:
parser = AbstractSourceParser("/tmp/file.csv").factory(self.type)
I think what I'm writing above doesn't make sense in code, but basically I want to retrieve the factory method 'after' passing in some data to the super class. How would this be done?
Currently, what I'm doing is the following, which I think is quite ugly (if not possibly incorrect) --
>>> _parser = AbstractSourceParser("/tmp/file.csv")
>>> parser = _parser.factory(_parser.source_type)
You could make factory() a regular instance method, and let it do the work:
class AbstractSourceParser(object):
def __init__(self, filepath):
self.source_type = get_type_from_filepath(filepath)
def factory(self, source_type=None):
if source_type is None:
source_type = self.source_type
source_type = source_type.upper() if source_type else None
if source_type == SourceType.CSV.upper():
return CSVSourceParser()
So that if you want the source type defined in the parent constructor, you can call factory() with no arguments. But you can still provide a different source_type if you want.
Does that get at what you're asking?
Related
I want to have abstract class Task and some derived classes like TaskA, TaskB, ...
I need static method in Task fetching all the tasks and returning list of them. But problem is that I have to fetch every task differently. I want Task to be universal so when I create new class for example TaskC it should work without changing class Task. Which design pattern should I use?
Let's say every derived Task will have decorator with its unique id, I am looking for function that would find class by id and create instance of it. How to do it in python?
There are a couple of ways you could achieve this.
the first and most simple is using the __new__ method as a factory to decide what subclass should be returned.
class Base:
UUID = "0"
def __new__(cls, *args, **kwargs):
if args == "some condition":
return A(*args, **kwargs)
elif args == "another condition":
return B(*args, **kwargs)
class A(Base):
UUID = "1"
class B(Base):
UUID = "2"
instance = Base("some", "args", "for", "the", condition=True)
in this example, if you wanted to make sure that the class is selected by uuid. you can replace the if condition to read something like
if a.UUID == "an argument you passed":
return A
but it's not really useful. since you have knowledge of the specific UUID, you might as well not bother going through the interface.
since I don't know what you want the decorator for, I can't think of a way to integrate it.
EDIT TO ADDRESS THE NOTE:
you don't need to have update it every time, if you do your expressions smartly.
let's say that the defining factor comes from a config file, that says "use class B"
for sub_classs in self.__subclasses__():
if sub_class.UUID == config.uuid:
return sub_class(*args, **kwargs) # make an instance and return it
the problem with that is that uuid is not useful to us as people. it would be easier to understand if instead we used a config.name to replace every place we have uuid in the example
I was fighting with this a lot of time and this is exactly what I wanted:
def class_id(id:int):
def func(cls):
cls.class_id = lambda : id
return cls
return func
def find_subclass_by_id(cls:type, id:int) -> type:
for t in cls.__subclasses__():
if getattr(t, "class_id")() == id:
return t
def get_class_id(obj)->int:
return getattr(type(obj), "class_id")()
class Task():
def load(self, dict:Dict) -> None:
pass
#staticmethod
def from_dict(dict:Dict) -> 'Task':
task_type = int(dict['task_type'])
t = find_subclass_by_id(Task, task_type)
obj:Task = t()
obj.load(dict)
return obj
#staticmethod
def fetch(filter: Dict):
return [Task.from_dict(doc) for doc in list_of_dicts]
#class_id(1)
class TaskA(Task):
def load(self, dict:Dict) -> None:
...
...
I have two classes, with similar methods (read, write, insert) but because of the file types the produce, their methods must be implemented differently. Ideally, I would like the user to initialize a base type and the appropriate subtype is returned based on keywords during instantiation:
c = SomeThing() # returns subclass of type 1 (set in default)
c = Something(flag=True) # returns the other subclass
Initially I tried putting a return statement in the __init__ of the base class, but appparently __init__ should return None, so not sure where to set this; should I just create a base class factory method that returns the appropriate type?:
class SomeThing:
def __init__(self, flag=False):
self.build(flag)
def build(self, flag):
if not flag:
return SubclassOne()
reutnr SubclassTwo()
Or is there a better way for dynamically binding the appropriate methods based on keywords passed at instantiation? I wouldnt think this would be ideal:
class SomeThing:
def __init__(self, flag=False):
if not flag:
setattr(self, 'write', self.write_one)
else:
setattr(self, 'write', self.write_two)
def write_one(self):
# stuff
def write_two(self):
# stuff
Because the user of the interface could simply access the other methods, and I wouldnt want to define each method outside the classes because then the user could say do from something import write_one which would be inappropriate behavior.
I'd recommend you go with a factory of sorts:
class Base(object):
# ...
class SomeThing(Base):
# ...
class OtherThing(Base):
# ...
def create_thing(flag = False):
if flag:
return SomeThing()
else:
return OtherThing()
I need a class router (for lack of a better word). The router needs to instantiate a class & call a function of that class instance based on variables passed to it.
How do I properly define the class function?
How do I properly call the class function?
Example code:
class ClassWorker1:
def function_1(self):
print('1a')
def function_2(self):
print('2a')
def function_3(self):
print('3a')
class ClassWorker2:
def function_1(self):
print('1b')
def function_2(self):
print('2b')
def function_3(self):
print('3b')
class ClassRouter(object):
def __init__(self, class_name, class_function):
self.class_instance = class_name()
self.class_function = class_function
self.main()
def main(self):
# how should I call the class function here?
self.class_instance.class_function()
return
a = 1
b = 1
if a == 1:
class_name = ClassWorker1
else:
class_name = ClassWorker1
if b == 1:
# Strings won't work as class function definition
# I won't know the class at this point. I will only know
# the shared function name at this point.
# how could this class function be defined directly?
class_function = 'function_1'
elif b == 2:
class_function = 'function_2'
else:
class_function = 'function_3'
ClassRouter(class_name, class_function)
I need a class router (for lack of a better word).
Are you sure you need a class for this ?
The router needs to instantiate a class & call a function of that class instance
When it belongs to a class or instance, a function is usually named a "method". Not really important but it makes things clearer. Also, an "instance" is obviously always, by definition, an instance of a class ;)
How do I properly define the class function?
How do I properly call the class function?
Does the router really have to be a class ? But anyway...
There are a couple distinct issues here (I of course assume you need something that's generic enough).
The first one is that your class (the one that will be instanciated by the "router") constructor may need some args - position or named or both. If it's the router's duty to instanciate the class (but should it be ?), you'll have to pass those args (both position and named) to the router. And since your router has to be generic (else it's useless) you cannot explicitely name these args in your router's constructor.
Hopefully, Python has a way to "unpack" tuples (for position args) and dicts (for named args) when calling a function, using respectively the * and ** operators at call time, ie:
def somefunc(arg1, arg2, arg3="foo", arg4=None):
print arg1, arg2, arg3, arg4
args = ("one", "two", "three")
kwargs = {"arg4": "four"}
somefunc(*args, **kwargs)
This let you pass arguments to a function in a generic way.
So if you want your router to be in charge of instanciating the "target" class, you'll have to support this:
class Router(object):
def __init__(self, cls, clsargs=None, clskwargs=None):
if clsargs is None:
clsargs = ()
if clskwargs is None:
clskwargs = {}
self._obj = cls(*clsargs, **clskwargs)
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker, clsargs=("foo",))
# or
r = Router(Worker, clskwargs={"name":"foo"})
Now note that at this point you really don't gain anything (except for more code) from having the router instanciating the Worker - since you need to have the Worker class and it's constructor's args to instanciate the router, you could as well just instanciate the Worker yourself and pass the Worker instance to the router:
Since you must have a reference to the class passed to the router (else you can't pass it ), you could as well
class Router(object):
def __init__(self, obj):
self._obj = obj
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker("foo"))
# or
r = Router(Worker(name="foo"))
The cases where it would make sense to have the router instanciate the worker are:
1/ if the Worker's constructor arguments are not known when the router is instanciated and are to be passed later (which requires a distinct router method to pass those args)
2/ if the Worker's instanciation is very costly and you're not even sure you'll really need it, in which case you want to wait until the router's "main" method is called to instanciate the worker.
The second issue is "how do I get the worker's method by name". This one has already been answered by Lukas: you use getattr(obj, attrname).
The third issue is "if my worker method needs arguments, how do I pass them". This is the same problem as with the worker's constructor arguments, so the solution is obviously the same. Depending on the concrete use case, you'll have to pass those args either when instanciating the router or when calling it's "main" method.
wrt/ this "main" method, remember that you can define your own callable types by implementing the __call__ method, ie
class NotAFunc(object):
def __init__(self, wot):
self.wot = wot
def __call__(self, count):
print self.wot * count
notafunc = NotAFunc("wot ? ")
notafunc(42)
So it might make sense to use this as your router's "main" method
Now do you really need a router class at all ? Python functions are object on their own (so a function can take a function and/or return a function), and moreover act as closures (a closure is a function that "captures" part of the environment where it's been defined):
def route(instance, methodname, methargs=None, methkwargs=None):
method = getattr(instance, methodname)
if methargs is None:
methargs = ()
if methkwargs is None:
methkwargs = {}
def func():
return method(*methargs, **methkwargs)
return func
class Worker(object):
def __init__(self, name):
self.name = name
def work(self, count):
return [self.name for i in range(count)]
r = route(Worker("foo"), "work", (42,))
print r()
Note that while I kept your "router" term, most of what I described above are known patterns. You may want to search for "proxy", "proxy method", and (for the last exemple) "partial evaluation".
You are looking for dynamic attribute lookup.
class C:
def c1(self, x):
return 2*x
instance = C()
method_name = 'c1'
method = getattr(instance, method_name)
print(method(1)) # call method and print result
You'll need to override the __new__ method of your (new-style!) class.
class ClassRouter(object):
def __new__(self, class_name, *args):
if arg=="Class1":
new_instance = ClassWorker1(*args)
new_instance.method()
return new_instance
elif arg=="Class2":
return ClassWorker2(*args)
class Spam(object):
#a_string = 'candy'
def __init__(self, sold=0, cost=0):
self.sold = sold
self.cost = cost
#staticmethod
def total_cost():
return True
#classmethod
def items_sold(cls, how_many):
#property
def silly_walk(self):
return print (self.a_string)
#silly_walk.setter
def silly_walk(self, new_string):
self.a_string = new_string.upper()
def do_cost(self):
if self.total_cost():
print('Total cost is:', self.cost)
.
from spam import Spam
def main ():
cost = 25
sold = 100
a_string = 'sweets'
sp = Spam(100, 25)
sp.do_cost()
sw = Spam.silly_walk(a_string)
sw.silly_walk()
if __name__ == '__main__':
main()
so im new to python and i don't understand how to use the setters and getters in this. so what i want to do is:
use #property to create a setter and getter for a property named silly_walk. Have the setter upper case the silly_walk string.
Show example code that would access the static method.
Show example code that would use the silly_walk setter and getter.
im getting very confused with what "self" does in the class and im not sure if what im doing is correct
update:
problem was the #classmethod not having a return and indentation error, so everything is fixed thanks everybody
self is convention. Since you're inside a class, you don't have functions there you have methods. Methods expect a reference to the object calling them as the first argument, which by convention is named self. You can call it anything you like.
class Foo(object):
def __init__(itsa_me_maaaario, name):
itsa_me_maaario.name = "Mario"
That works just as well.
As for the rest of your code -- what's your QUESTION there? Looks like your setter is a bit weird, but other than that it should work mostly okay. This is better:
class Spam(object): # inherit from object in py2 for new-style classes
def __init__(self, a_string, sold=0, cost=0) # put the positional arg first
...
#staticmethod
def total_cost():
# you have to do something meaningful here. A static method can't access
# any of the objects attributes, it's really only included for grouping
# related functions to their classes.
#classmethod
def items_sold(cls, how_many):
# the first argument to a classmethod is the class, not the object, so
# by convention name it cls. Again this should be something relevant to
# the class not to the object.
#property
def silly_walk(self):
return self.a_string
# don't call itself.
#silly_walk.setter
def silly_walk(self, new_string):
self.a_string = new_string
# it really just hides the attribute.
For instance I have a class I built to abstract a computer system I'm in charge of. It might be something like:
class System(object):
type_ = "Base system"
def __init__(self, sitenum, devicenum, IP):
self._sitenum = sitenum
self._devicenum = devicenum
self._IP = IP
# the leading underscores are a flag to future coders that these are
# "private" variables. Nothing stopping someone from using it anyway,
# because System()._IP is still that attribute, but it makes it clear
# that they're not supposed to be used that way.
#staticmethod
def ping_system(IP):
subprocess.call(["ping",IP], shell=True) # OH GOD SECURITY FLAW HERE
# group this with Systems because maybe that's how I want it? It's an
# aesthetic choice. Note that this pings ANY system and requires an
# argument of an IP address!
#classmethod
def type_of_system(cls):
return cls.type_
# imagine I had a bunch of objects that inherited from System, each w/
# a different type_, but they all inherit this....
#property
def description(self):
return "Site {}, Device {} # {}".format(self._sitenum,
self._devicenum,
self._IP)
#description.setter
def description(self, *args):
if len(args) == 3:
self._sitenum, self._devicenum, self._IP = args
elif len(args) == 1 and len(args[0]) == 3:
self._sitenum, self._devicenum, self._IP = args[0]
else:
raise ValueError("Redefine description as Sitenum, Devicenum, IP")
Example:
computer = System(1, 1, '192.168.100.101')
System.ping_system('192.160.100.101') # works
computer.type_of_system # "Base system"
computer.description # "Site 1, Device 1 # 192.168.100.101"
new_description = [1, 2, '192.168.100.102']
computer.description = new_description
# invokes description.setter
computer._devicenum # is 2 after the setter does its magic.
I've written some code that allows the term 'job' to be used universally to perform a unique task. The specific jobs can be chosen through setting an initial variable "job_type". From that initial variable a particulay subclass is chosen to perform the appropriate job. Maybe the code will make more sense :)
if __name__=='__main__':
# these variables would normally be called in from a config file
job_type = 'job1'
uni_var = 10
job_select = superClass(job_type, uni_var)
job_select.job()
class superClass(object):
def __init__(self, job_type, uni_var):
self.job_type = job_type
self.uni_var = uni_var
if self.job_type == 'job1':
self.jobChoice = option1()
else:
self.jobChoice = option2()
# This is the definition called by the main function it then
# redirects the request to the appropriate job sub class
def job(self):
self.jobChoice.job()
class option1(superClass):
def __init__(self):
pass
def job(self):
print 'job option 1'
print uni_var
class option2(superClass):
def __init__(self):
pass
def job(self):
print 'job option 2'
print uni_var
The thought behind this code was to allow a single/constant 'main' function, to action a variety of unique tasks based purely on the variable 'job_type'. Which it seems to be doing fine.
My question (as a very inexperienced coder) is, have I gone about this the right way or is there a better way to do things?
Also, have I set up the variable 'uni_var' correctly in the superClass to be correctly shared across all/any superClass subclasses?
Thanks.
I suspect that what you really want is to use the Factory Method Pattern here.
You could change your code to something like this:
if __name__=='__main__':
# these variables would normally be called in from a config file
job_type = 'job1'
uni_var = 10
job_select = superClass.optionFactory(job_type, uni_var)
job_select.job()
class superClass(object):
def __init__(self, job_type, uni_var):
self.job_type = job_type
self.uni_var = uni_var
# This is the definition called by the main function it then
# redirects the request to the appropriate job sub class
def job(self):
raise NotImplementedError()
#staticmethod
def optionFactory(job_type, uni_var):
"Return an instance of superClass based on job_type and uni_var."
if job_type == "job1":
return option1(job_type, uni_var)
else:
return option2(job_type, uni_var)
class option1(superClass):
def __init__(self, job_type, uni_var):
super(option1, self).__init__(job_type, uni_var)
def job(self):
print 'job option 1'
print uni_var
class option2(superClass):
def __init__(self, job_type, uni_var):
super(option2, self).__init__(job_type, uni_var)
def job(self):
print 'job option 2'
print uni_var
However, notice that this implementation will require that superClass be changed every time a new subclass is created. Another alternative would be to make the optionFactory method a standalone function (rather than a method of superClass). Like this:
if __name__=='__main__':
# these variables would normally be called in from a config file
job_type = 'job1'
uni_var = 10
job_select = optionFactory(job_type, uni_var)
job_select.job()
class superClass(object):
def __init__(self, job_type, uni_var):
self.job_type = job_type
self.uni_var = uni_var
# This is the definition called by the main function it then
# redirects the request to the appropriate job sub class
def job(self):
raise NotImplementedError()
class option1(superClass):
def __init__(self, job_type, uni_var):
super(option1, self).__init__(job_type, uni_var)
def job(self):
print 'job option 1'
print uni_var
class option2(superClass):
def __init__(self, job_type, uni_var):
super(option2, self).__init__(job_type, uni_var)
def job(self):
print 'job option 2'
print uni_var
def optionFactory(job_type, uni_var):
"Return an instance of superClass based on job_type and uni_var."
if job_type == "job1":
return option1(job_type, uni_var)
else:
return option2(job_type, uni_var)
Don't use a class as a factory, it's silly. You just need a consistent interface across implementations.
class JobA(object):
def do_job(self, arg):
print 'job a', arg
class JobB(object):
def do_job(self, arg):
print 'job b', arg
job_types = {
'job1': JobA, 'job2': JobB
}
job_type = 'job1'
uni_var = 10
job = job_types[job_type]()
job.do_job(uni_var)
Hell, if the jobs don't keep state, they shouldn't be classes, either, but functions instead.
def job_a(arg):
...
def job_b(arg):
...
job = job_types[job_type]
job(uni_var)
You are actually not using inheritance at all in your original code, and your code contains a severe bug, but masks it from appearing.
You create an instance of superClass, which stores job_type and uni_var, and then instantiates either option1 or option2 and stores a reference to that. The "subclasses" are initialised with no data (so they have no job_type or uni_var attributes, and you have to override __init__ to do-nothing methods to avoid error).
When you call job_select.job(...), superClass explicitly delegates to its job_choice attribute. So there's no useful inheritance going on; you've overridden everything about superClass in your subclasses, and instead having job_select be an instance of different sub-classes depending on the job_type and using method resolution to call the right code, job_select is always a superClass which contains an option1 or option2 and explicitly delegates to it.
The serious bug I mentioned: neither option1 nor option2 actually contains any information about the job, so their job methods can't do anything interesting. You call print uni_var, which doesn't work (it would normally be print self.uni_var to get the uni_var of this job), but seems to work because you have a global called uni_var. As soon as you start doing anything more complicated this scheme will fall over badly.
#srgerg's answer is a good example of what you could do to actually use inheritance and factory functions to solve your problem. #CatPlusPlus's answer is a good example of how you can use more appropriate tools for the very simple code in this example (may not be what you need if your real requirements are more complex what you've got written at the moment).