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()
Related
Was wondering if there was a way to set a class attribute to a specific instance from within the class definition. For example,
class Value:
def __init__(self, x):
self.x = x
# Something like
# half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
I'm also aware I can easily set it outside the class that seems a bit more bulky and error prone, like this
class Value:
def __init__(self, x):
self.x = x
Value.half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
No. At the time the body of the class is being evaluated, the class doesn't yet exist. A class statement is a declarative syntax for calling a metaclass:
class Value:
def __init__(self, x):
self.x = x
is roughly equivalent to
def init(self, x):
self.x = x
Value = type('Value', (object,), {'__init__': init})
Your class attribute would have to be a member of the dict passed as the third argument, which has to be fully defined before type is called.
not quite, but you can make a class method that return a new instance of your class in whatever way you want with the classmethod decorator
>>> class Value:
def __init__(self, x):
self.x=x
def __repr__(self):
return f"{type(self).__name__}({self.x})"
#classmethod
def half(cls):
return cls(0.5)
>>> Value(10)
Value(10)
>>> Value.half()
Value(0.5)
>>>
look like in py3.9 you can combine it with the property decorator to accomplish just that, see linked documentation above (but I don't have it at the moment)
Simply, you can't because the class hasn't yet existed. But you can use either metaclass or class decorator to achieve the same goal as the following shows:
#Metaclass
class Meta(type):
def __init__(cls, clsname, clsbases, clsdict):
cls.half = cls(0.5)
class Value(metaclass=Meta):
def __init__(self, x):
self.x = x
#Decorator
def decorator(cls):
cls.half = cls(0.5)
return cls
#decorator
class Value2:
def __init__(self, x):
self.x = x
print(Value.half.half.x)
print(Value.half.x)
print(Value2.half.half.x)
print(Value2.half.x)
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
I've got a class which contains a number of lists where whenever something is added to one of the lists, I need to trigger a change to the instance's state. I've created a simple demonstration class below to try to demonstrate what I'm trying to do.
Suppose I have a class like this:
class MyClass:
added = False
def _decorator(self, f):
def func(item):
added = true
return f(item)
return func
def __init__(self):
self.list = [1, 2, 3]
self.list.append = self._decorator(self.list.append)
Since a list is built in, I cannot change it's .append method
cls = MyClass() #gives me an AttributeError since '.append' is readonly
Ideally, I could do the following:
cls = MyClass()
cls.list.append(4)
cls.added #would be true
How should I go about this? Would subclassing list allow me to change it's behavior in this way? If so, how would I pass in the class's state without changing the methods signature?
Thanks!
You cannot monkey-patch builtins, so subclassing is the only way (and actually better and cleaner IMHO). I'd go for something like this:
class CustomList(list):
def __init__(self, parent_instance, *args, **kwargs):
super(CustomList, self).__init__(*args, **kwargs)
self.parent_instance = parent_instance
def append(self, item):
self.parent_instance.added = True
super(CustomList, self).append(item)
class MyClass(object):
added = False
def __init__(self):
self.list = CustomList(self, [1,2,3])
c = MyClass()
print c.added # False
c.list.append(4)
print c.added # True
Would this suit your needs?
class MyClass(object):
added = False
def __init__(self):
self.list = [1,2,3]
def append(self, obj):
self.added = True
self.list.append(obj)
cls = MyClass()
cls.append(4)
cls.added #true
It might be helpful to know what exactly you're trying to achieve.
I have a class like this:
class MyClass(object):
def f_1(self,x):
return foo(x, self.property_1)
def f_2(self,x):
return foo(x, self.property_2)
The idea is that multiple functions f_n have a common structure, but depend on different properties property_n of the class.
I look for a more compact way to define those f_n in the __init__? I think of something like
class MyClass(object):
def __init__(self):
self.f_1 = self.construct_function(self.property_1)
self.f_2 = self.construct_function(self.property_2)
def construct_function(self, property):
# ???
That is what I have in mind, but I dont know how to define this construct_function. It is important that 'property' is of a point-by-value type.
Edit:
I simplified Martijn's very good answer to this solution, which works fine:
def construct_function(property_name):
def f_n(self, x):
return foo(x, getattr(self, property_name))
return f_n
class MyClass2(object):
f_1 = construct_function('property_1')
f_2 = construct_function('property_2')
Just wanted to mention it here, as multiline comments are not allowed...
If you want to generate these methods per class, use a class decorator:
def property_functions(**properties):
def construct_method(prop):
def f_n(self):
return foo(getattr(self, prop))
return f_n
def class_decorator(cls):
for name, prop in properties.iteritems():
setattr(cls, name, construct_method(prop))
return cls
return class_decorator
then use it like:
#property_functions(f_1='property_1', f_2='property_2')
class MyClass(object):
property_1 = 'foo'
property_2 = 'bar'
Demonstration:
>>> def foo(value): print value
...
>>> #property_functions(f_1='property_1', f_2='property_2')
... class MyClass(object):
... property_1 = 'foo'
... property_2 = 'bar'
...
>>> mc = MyClass()
>>> mc.f_1()
foo
>>> mc.f_2()
bar
You can have a look at getattr or getattribute . They allow you dynamically create and reference attributes. For ex
It works something like this:
class foo:
def __init__(self):
self.a = "a"
def __getattr__(self, attribute):
return "You asked for %s, but I'm giving you default" % attribute
>>> bar = foo()
>>> bar.a
'a'
>>> bar.b
"You asked for b, but I'm giving you default"
>>> getattr(bar, "a")
'a'
>>> getattr(bar, "b")
"You asked for b, but I'm giving you default"
I have a Python object which has certain attributes that are set after the constructor is called. For example,
def Student(object):
def __init__(name, address=None):
self.name = name
self.address = address
stud = Student("John")
stud.address = "123 Main St. New York, NY"
I would like to be able to have a function be called when the address attribute is set which will do things such as reformat the address or do a lookup and add in the zip code, etc. Is there a way to accomplish this within the object's definition or should I just have to do that myself every time I set the address attribute?
What you probably need is a property concept.
class C(object):
def __init__(self, x):
self._x = x
def get_x(self):
return self._x
def set_x(self, x):
self._x = x
x = property(get_x, set_x)
obj = C(5)
obj.x = 6 # set
print obj.x # get
See this link for more details: http://snippets.dzone.com/posts/show/954
You're looking for property().
def Student(object):
def _get_address(self):
...
def _set_address(self):
...
address = property(_get_address, _set_address)