I think to have a use case for the factory pattern method in python and have two ideas how to implement it (see below). Both work, but are they really the same? Option2 looks much clearer to me, despite having a few more lines. Is my gut feeling right? Or is there a third better option?
Option 1
def inner_factory_func(a):
class Inner:
def __init__(self, b):
self.a = a
self.b = b
def call(self):
return self.a + self.b
return Inner
inner_factory = inner_factory_func('a')
inner = inner_factory('b')
res = inner.call() # ='ab'
Option 2
class Inner:
def __init__(self,a,b):
self.a = a
self.b = b
def call(self):
return self.a+self.b
class InnerFactory:
def __init__(self,a ):
self.a = a
def create(self,b) -> Inner2:
return Inner(self.a,b)
inner_factory = InnerFactory('a')
inner = inner_factory.create('b')
res = inner.call() # ='ab'
Related
I want to access a variable created inside some method from another method. For instance:
class text():
def __init__(self, text):
self.txt = text
def sp1(self):
self.a = self.txt.split(',')[0]
self.b = self.txt.split(',')[1]
return self.a
def sp2(self):
return self.b
Now when I try to apply this like in:
T = text('I woke up early, it is school today')
print(T.sp2())
I get an error that 'text' object has no attribute 'b'
I don't understand where the problem seems to be?
Perhaps you mean:
T = text('I woke up early, it is school today')
T.sp1()
print(T.sp2())
So I guess the solution in this case would be to call sp1 inside sp2
....
def sp2(self):
self.sp1()
return self.b
You did not define self.b prior to calling it.
(Edited to remove false assumption that you can only define the attributes in __init__)
class text():
def __init__(self, text):
self.txt = text
self.a = self.txt.split(',')[0]
self.b = self.txt.split(',')[1]
def sp1(self):
return self.a
def sp2(self):
return self.b
class text:
def init(self, text):
self.txt = text
def sp1(self):
self.a = self.txt.split(',')[0]
self.b = self.txt.split(',')[1]
return self.a
def sp2(self):
self.sp1()
return self.b
I am looking for a way to init a variable in a class. This variable is dependent of other variables that I init in the same class too. Here is an example.
Class class():
def __init__()
self.a = None
self.b = None
self.sum = self.a + self.b
def change_a_and_b()
self.a = input("a = ")
self.b = input("b = ")
def print_sum()
print(self.sum)
This is a simplified example. In my program self.sum is a complicated calculation which I want to use like a "shortcut" so I don't need to write it a lot of times. The problem is I input the variables after the init function. I just don't want to execute self.sum = self.a + self.b when I run __init__. I think there is a do_thing parameter, but I don't know how to use it.
You can make sum a property:
class my_class():
def __init__(self)
self.a = None
self.b = None
#property
def sum(self):
return self.a + self.b
def change_a_and_b(self)
self.a = input("a = ")
self.b = input("b = ")
def print_sum(self)
print(self.sum)
In my python script I have defined a class similar to the following (admittedly bogus) class:
import copy
class Example:
def __init__(self, a, b):
self.a = a
self.b = b
self.__default__ = copy.deepcopy(self.__dict__)
self.t = 0
self.d = False
def do(self):
self.a += self.b - self.t
self.t += 1
if self.t == self.b:
self.d = True
return self.a
def reset(self):
self.__init__(**self.__default__)
Now, I would like to pass an instance of this class to my main function and repeatedly reset the instance to its default state. Despite having a look here, here, here and here, I couldn't get it going. The working example below gives the desired result, yet resets the instance in the main function explicitly. The dysfunctional example is one of my many tries to make it work using a reset method.
# working example:
def main(x):
agg = []
for i in range(x):
klass = Example(1, 3)
while not klass.d:
a = klass.do()
agg.append(a)
return agg
# dysfunctional example:
def main2(klass, x):
agg = []
for i in range(x):
klass.reset()
while not klass.d:
a = klass.do()
agg.append(a)
return agg
Then main(5) gives
res = main(5)
print(res)
>>> [4, 6, 7, 4, 6, 7, 4, 6, 7, 4, 6, 7, 4, 6, 7]
whereas
ex = Example(1, 3) # default state
res = main2(ex, 5)
print(res)
throws the error: TypeError: __init__() got an unexpected keyword argument '__default__'
Since I would like to avoid having to re-instantiate the class in the main script anew for different reasons, I would be grateful if someone could help me out with the reset method.
How about something like that:
class Example:
def __init__(self, *args, **kwargs):
"""This stores the default state then init the instance using this default state"""
self.__default_args__ = args
self.__default_kwargs__ = kwargs
self.init(*args, **kwargs)
def do(self):
"""Do whatever you want """
self.a += self.b - self.t
self.t += 1
if self.t == self.b:
self.d = True
return self.a
def init(self, a, b):
"""Inits the instance to a given state"""
self.a = a
self.b = b
self.t = 0
self.d = False
return self
def reset(self):
"""Resets the instance to the default (stored) state"""
return self.init(*self.__default_args__, **self.__default_kwargs__)
Here is an implementation using context manager:
class Example:
def __init__(self, a, b):
self.a = a
self.b = b
self.t = 0
self.d = False
def do(self):
self.a += self.b - self.t
self.t += 1
if self.t == self.b:
self.d = True
return self.a
def __enter__(self):
self._a = self.a
self._b = self.b
def __exit__(self, *args):
self.t = 0
self.d = False
self.a = self._a
self.b = self._b
def main2(klass, x):
agg = []
for i in range(x):
with klass:
while not klass.d:
a = klass.do()
agg.append(a)
return agg
ex = Example(1, 3)
res = main2(ex, 5)
print(res)
A reusable way to do this would be to implement a Resettable class to be inherited.
Resettable
class Resettable:
def __init__(self, *args, **kwargs):
self.__args__ = args
self.__kwargs__ = kwargs
def reset(self):
self.__init__(*self.__args__, **self.__kwargs__)
Usage
Using a property to define an attribute that entirely depends on other attributes will also smoothen the process of resetting. This idea of having a single source of truth is generally a helpful paradigm for states that need to go back and forth in time.
class Example(Resettable):
def __init__(self, a=0):
self.a = a
super().__init__(a)
def do(self):
self.a += 1
return self.a
#property
def d(self):
return self.a > 3 # or any other condition
def main(instance, x):
agg = []
for _ in range(x):
instance.reset()
while not instance.d:
a = instance.do()
agg.append(a)
return agg
print(main(Example(), 3)) # [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
The underlying assumption of the Resettable class is that the arguments passed to the constructor contain all the information needed to reset, using properties make that assumption easier to satisfy.
I have a need to allow the user to define a function that processes data in an object (the wisdom and security implications in this have been discussed at length in another question and would just be duplicate comments here.)
I'd like the function to act just like any other method. That is
def my_method(self):...
Would be invoked with:
obj_handle.my_method()
I almost have this achieved below except that the function that results need to be explicitly passed self as an argument, rather than receiving it as the first argument as is typical for a method.
You can see this in property p where I have the odd self.process(self) call.
I imagine that I need to provide something to exec that is like the globals() dictionary, but I'm not certain of several things:
Is this correct?
What is the equivalent of globals() in a class?
Does this solve the problem? If not what do I need to do?
So the question is, how do I get an exec() defined function to act as an object's method?
class test:
def __init__(self, a, b):
self.a=a
self.b=b
#property
def p(self):
return self.process(self)
def set_process(self,program):
func_dict={}
proc_fun = exec(program,func_dict)
setattr(self,'process',func_dict['process'])
def process(self):
return self.a+self.b
t=test(1,2)
prog = '''\
def process(self):
return self.a * self.b
'''
t.set_process(prog)
t.p
Answered in #juanpa.arrivillaga's comment above:
Set the function on the class if you want its descriptor protocol to work and bind tye instance when called on an instance. So one solution just make your set_process a classmethod. – juanpa.arrivillaga 1 hour ago
Working result
class test:
def __init__(self, a, b):
self.a=a
self.b=b
#property
def p(self):
return self.process()
#classmethod
def set_process(cls,program):
func_dict={}
proc_fun = exec(program,func_dict)
setattr(cls,'process',func_dict['process'])
def process(self):
return self.a+self.b
t=test(1,2)
prog = '''\
def process(self):
return self.a * self.b
'''
test.set_process(prog)
t.p
If you want to operate on instances rather than classes:
import types
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
#property
def p(self):
return self.process()
def set_process(self, program):
d = dict()
exec(program, d)
self.process = types.MethodType(d["process"], self)
def process(self):
return self.a + self.b
prog = '''\
def process(self):
return self.a * self.b
'''
t = Test(1, 2)
t.set_process(prog)
print(t.p)
t = Test(1, 2)
print(t.p)
I want to pass a function to a class when I initialize it. Here's a toy example I came up with and it works:
def addition(self):
return self.a + self.b
def multiplication(self):
return self.a * self.b
class Test:
def __init__(self, a, b, fcn):
self.a = a
self.b = b
self.fcn = fcn
t = Test(3, 3, addition)
print t.fcn(t)
t = Test(3, 3, multiplication)
print t.fcn(t)
Is it possible to simply call t.fcn() as you would any other class method?
did you try it?
the answer is yes
def do_op(x,y,fn):
return fn(x,y)
def add(a,b):
return a+b
print do_op(5,4,add)
same with a class
class whatever:
def __init__(self,fn):
self.fn = fn
def do_it(self,*args,**kwargs):
return self.fn(*args,**kwargs)
#if you wanted the fn to have self as the first argument
#return self.fn(self,*args,**kwargs) #just pass self as first argument
x = whatever(add)
print x.do_it(5,8)
further along what you are asking for (if im reading it right)
def add(self):
return self.a + self.b
class whatever:
def __init__(self,fn,a,b):
self.__dict__[fn.__name__] = fn
self.a,self.b = a,b
def do_it(self):
return self.fn(self)
x = whatever(add,6,7)
x.do_it()
or perhaps you want something like
from functools import partial
def add(self):
return self.a + self.b
class whatever:
def __init__(self,fn,a,b):
self.__dict__[fn.__name__] = partial(fn,self)
self.a,self.b = a,b
x = whatever(add,5,6)
x.add()
this kind of introspection is somewhat risky in deployed code ...