how to pass method name as a parameter in python class - python

This is my code, my intention is to pass the method name as a parameter when I initialize the object and I want to run the method 'num' (second argument) of times. Basically get n number of results (as mentioned in 2nd argument).
class Foo(object):
faker = Faker()
def __init__(self, custom_method, num=1):
self.values = []
self.custom_method = custom_method
self.num = num
for x in self.num:
self.custom_method = self.values.append(custom_method)
def random_first_name(self):
self.custom_method = self.faker.first.name()
return self.custom_method
def random_phone(self):
self.custom_method = self.faker.random.phone()
return self.custom_method
b = Foo(random_first_name, 1)
c = Foo(random_phone,2)

I guess that you may want to use the function getattr.
class Foo(object):
faker = Faker()
def __init__(self, custom_method, num=1):
self.custom_method = custom_method
self.num = num
#property # Briefly, the property decorator makes the job of calling the callable for you. I.e. There is no need to do self.method(), self.method is enough.
def random_first_name(self):
return self.faker.first.name()
#property
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self):
return [getattr(self, self.custom_method)\
for _ in range(self.num)]
I cannot instantiate this class, but this could be used as follows:
>>> foo1 = Foo('random_first_name', 1)
>>> foo1.call_method_num_times()
['John']
>>> foo2 = Foo('random_phone', 2)
>>> foo2.call_method_num_times()
['0123456789', '9876543210']
To (even more) reorganize your class in a (subjectively) better fashion, I would do
class Foo(object):
def __init__(self):
self.faker = Faker()
#property
def random_first_name(self):
return self.faker.first.name()
#property
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self, custom_method, num=1):
return [getattr(self, custom_method)\
for _ in range(num)]
Thus allowing you for instantiating Foo only once
>>> foo = Foo()
>>> foo.call_method_num_times('random_first_name')
['John']
>>> foo.call_method_num_times('random_phone', 2)
['0123456789', '9876543210']
If you are not comfortable with the use of the python native property descriptor, you can keep your two methods as explicite ones. In this case, you would define the class Foo as follows
class Foo(object):
def __init__(self):
self.faker = Faker()
def random_first_name(self):
return self.faker.first.name()
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self, custom_method, num=1):
return [getattr(self, custom_method)()\
for _ in range(num)]
Which would change nothing in ways of using Foo
>>> foo = Foo()
>>> foo.call_method_num_times('random_first_name')
['John']
>>> foo.call_method_num_times('random_phone', 2)
['0123456789', '9876543210']

Related

Inheriting variables from multiple class functions with super()

class base():
def __init__(self):
self.var = 10
def add(self, num):
res = self.var+num
return res
class inherit(base):
def __init__(self, num=10):
x = super().add(num)
a = inherit()
print(a)
Hello,
I'm learning about inheritance and super(). When running this, the error AttributeError: 'inherit' object has no attribute 'var' is returned. How can I inherit the init variables too?
You first need to call super constructor because you did not define var in your base class constructor.
Working version of your code (though you should probably add var in base __init__)
class Base:
def __init__(self):
self.var = 10
def add(self, num):
res = self.var + num
return res
class Inherit(Base):
def __init__(self, num=10):
super().__init__()
x = super().add(num)
a = Inherit()
print(a)
one possible solution
class Base:
def __init__(self, var=10):
self.var = var
def add(self, num):
res = self.var + num
return res
class Inherit(Base):
pass
a = Inherit()
a.add(0) # replace 0 with any integer

Define partial specialization of Class

I have a bunch of classes that all vary only by a string and a function, like so:
class ClassOne:
__init__(self, x):
self._x = x
self._my_str = 'hello'
def greet():
fun1(self._x)
return self._my_str
class ClassTwo:
__init__(self, x):
self._x = x
self._my_str = 'howdy'
def greet():
fun2(self._x)
return self._my_str
I would love to be able to define
ClassOne = ClassTemplate('hello', fun1)
ClassTwo = ClassTemplate('howdy', fun2)
So that ClassOne and ClassTwo still act as normal classes, i.e. can be imported into other modules and the likes. I realise this is probably a standard technique, but not knowing its name I cannot manage to Google it (that also explains my inability to properly name the question)
I realise I can take them in as part of the init, but the choice of function and string is not obvious for the user at all, so I would prefer to name them.
Edit: It seems people misunderstand what I seek. Continuing the example I want to be able to do
first_instance = ClassOne(3.14)
second_instance = ClassTwo(2.71)
first_instance.greet()
I.e. ClassOne and ClassTwo need to be proper class definitions.
_my_str can be class attribute, which you can set using
__init_subclass__ provided by an appropriate base class. We'll make fun a static method so that the user doesn't have to define fun specially.
class BaseClass:
def __init_subclass__(cls, my_str=None, fun=None):
cls._my_str = my_str
cls._fun = staticmethod(fun)
def __init__(self, x):
self._x = x
def greet(self):
self._fun(self._x)
return self._my_str
class ClassOne(BaseClass, my_str='hello', fun=fun1):
pass
class ClassTwo(BaseClass, my_str='howdy', fun=fun2):
pass
(If you just used cls._fun = fun, then self._fun(self._x) would be equivalent to type(self)._fun(self, self._x), rather than the intended type(self)._fun(self._x).)
Alternately, you can simply declare the class attributes explicitly, though now the caller is responsible for correctly defining fun as a static method.
class BaseClass:
def __init__(self, x):
self._x = x
def greet(self):
self._fun(self._x)
return self._my_str
class ClassOne(BaseClass):
_my_str = 'hello'
fun = staticmethod(fun1)
class ClassTwo(BaseClass):
my_str = 'howdy'
fun = staticmethod(fun2)
In either case, you can defer setting the class attributes:
class ClassThree(BaseClass):
pass
# time passes
ClassThree._my_str = "g'day"
ClassThree.fun = staticmethod(fun3)
You can make the classes take in parameters when creating them
Update:
To name parameters, set a default value
class ClassThing:
def __init__(self, x, string=None, function=None):
self.x = x #whatever x is
self.string = string
self.function = function
def greet(self):
self.function(self.x)
return self.string
Then you can do
>>> classthing1 = ClassThing(32, string = "hello1", function = lambda x: print(f"LOL {x}"))
>>> classthing2 = ClassThing(129, string = "hello2", function = lambda x: print(f"LOL lmao {x}"))
>>> classthing1.greet()
LOL hello1
32
>>> classthing2.greet()
LOL lmao hello2
129
Edited because I was way off
As you don't want to pass the function in the init you can do this
class BaseClass:
def __init__(self, x):
self._x = x
self._my_str = 'hello'
self.fun = None
def greet(self):
self.fun(self._x)
return self._my_str
def fun(x):
print(x)
def ClassTemplate(s, f):
res = BaseClass(s)
res.fun = f
return resr
a = ClassTemplate('toto', fun)
a.greet()

Limit of decorator as class compared to decorator as function

I want to make sure that I understood correctly how decorator as class works.
Let's say i have a decorator as a function that add an attribute to an object
def addx(obj):
obj.x = 10
return obj
#addx
class A:
pass
assert A.x == 10
Is it possible to write the same decorator as a class decorator? since the class decorator can't return the object itself with __init__
class addx:
def __init__(self, obj):
obj.x = 10
# ???
You could write an equivalent class-based decorator like this...
class addx:
def __new__(self, obj):
obj.x = 10
return obj
#addx
class A:
pass
assert A.x == 10
...but I don't think this really gets you anything. The utility of a class-based decorator becomes more apparent when your goal is to modify objects of class A, rather than class A itself. Compare the following two decorators, one function based and one class based:
def addx_func(kls):
def wrapper():
res = kls()
res.x = 10
return res
return wrapper
class addx_class:
def __init__(self, kls):
self.kls = kls
def __call__(self):
res = self.kls()
res.x = 10
return res
#addx_func
class A:
pass
#addx_class
class B:
pass
a = A()
assert a.x == 10
b = B()
assert b.x == 10

python object building using multi-inheritance

I want to build an object dynamically which allow use to mix the class properties in whichever way they like base on multiple inheritance. This is the expected behaviour. These classes are dataclasses so there won't be many methods in them, mostly data properties.
class Foo():
def bar(self, x):
return x
class FooA(Foo):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
class FooB(Foo):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
class FooC(FooA, FooB):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
f = FooC()
f.bar('S') # SFooBFooAFooC
However this code violate the DRY principle in broad daylight, hence I want to avoid the bar method completely, if there is no special operations in the current class.
Ideally I want something like
#bar_wrapper
class FooA(Foo):
pass
# OR
class FooA(Foo):
__metaclass__ = BarBase
Instead of this full implementation
class FooA(Foo):
def bar(self, x):
p = super().bar(x)
p += __class__.__name__
return p
Essentially is there a way that I extract the middle layer class information in a multi-level inheritance class through a decorator or metaclass (the two options that I can think of)? Anyone has any idea on how to do this?
Write a class decorator that adds the bar method to the class:
def bar_wrapper(cls):
def bar(self, x):
p = super(cls, self).bar(x)
p += cls.__name__
return p
bar.__module__ = cls.__module__
bar.__qualname__ = '{}.{}'.format(cls.__qualname__, bar.__name__)
cls.bar = bar
return cls
class Foo():
def bar(self, x):
return x
#bar_wrapper
class FooA(Foo):
pass
#bar_wrapper
class FooB(Foo):
pass
#bar_wrapper
class FooC(FooA, FooB):
pass
f = FooC()
print(f.bar('S')) # SFooBFooAFooC

Influencing class variables with functions that are method's variables

Say I have a class with a method that takes a function as an argument. Is there any way to make this function change inclass variables?
def f():
# something here to change MyClass.var
class MyClass:
def __init__():
self.var = 1
def method(self, func):
#does something
func()
obj = MyClass()
obj.method(f)
print(obj.var)
Simply pass the internal reference of your class - self - into the function:
>>> class Class:
def __init__(self):
self.var = 1
def method(self, func):
func(self)
>>> def func(inst):
inst.var = 0
>>> cls = Class()
>>> cls.var
1
>>> cls.method(func)
>>> cls.var
0
>>>
On a related side note, I'd argue that it'd be cleaner and clearer to actually make your function a method of your class:
>>> from types import MethodType
>>>
>>> def func(self):
self.var = 0
>>> class Class:
def __init__(self):
self.var = 1
>>> cls = Class()
>>> cls.var
1
>>> cls.func = MethodType(func, cls)
>>> cls.func()
>>> cls.var
0
>>>
This should work:
def f(obj):
obj.var = 2
class MyClass:
def __init__(self):
self.var = 1
def method(self, func):
# does something
func(self)
obj = MyClass()
obj.method(f)
print(obj.var) # --> 2
Since the function f is defined outside the scope of the class, it can not access the class variable. However you can pass the class variable as an argument to f, and in that case it will be able to do any operation on it.
def f(x):
return x**2 # just for the demonstration. Will square the existing value\\
# of the class variable
class MyClass:
def __init__(self):
self.var = 2
def method(self, func):
#does something
self.var = func(self.var)
obj = MyClass()
obj.method(f)
print(obj.var)
>>> 4

Categories