I have a class A defining basics behaviour of my object and a class B inheriting from list and C inheriting from str
class A(object):
def __init__(self, a):
self.a = a
class B(list, A):
def __init__(self, inputs, a):
A.__init__(self, a)
return list.__init__(self, [inputs])
class C(str, A):
def __new__(self, input, a):
return str.__new__(self, input)
def __init__(self, inputs, a):
A.__init__(self, a)
def __init__(self, input, a):
A.__init__(self, a)
What I'd like is that the user build object B or C which behaves like a list or a str, those classes just have metadata usefull for our application but not for the user ... using class B is easy, if I want to change the values, I can clear it or append new values ... but how can I modify the value of a C object. I checked setattr but this one required an attribute name ...
thanks,
Jerome
This works:
>>> class A(object):
... def __init__(self, a):
... self.a = a
...
>>> class B(list, A):
... def __init__(self, inputs, a):
... A.__init__(self, a)
... return list.__init__(self, [inputs])
...
>>> class C(str, A):
... def __new__(self, input, a):
... return str.__new__(self, input)
... def __init__(self, inputs, a):
... A.__init__(self, a)
...
>>> c = C('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __new__() takes exactly 3 arguments (2 given)
>>> c = C('foo', 1)
>>> c
'foo'
>>> c.a
1
>>> c.a = 2
>>> c.a
2
>>>
You can change the metadata on a C instance. Like str, you can't change the value of the characters it contains.
If you want a mutable string, you're going to have to create that in pure python. However, given that everyone else gets by without that, consider whether you can use the built in facilities, such as TextStream.
You can't. Strings are immutable - you cannot change their value once created. Lists are mutable, which is why you can change their value (contents) after creation.
Related
This question already has answers here:
Class that acts as mapping for **unpacking
(3 answers)
Implement packing/unpacking in an object
(4 answers)
Closed last year.
I'd like to make a class that unpacks it's objects like a dictionary.
For example, with a dictionary you can do this
foo = {
"a" : 1
"b" : 2
}
def bar(a,b):
return a + b
bar(**foo)
outputs 3
And I'd like to be able to do this
class FooClass:
def __init__(self):
self.a = a
self.b = b
f = FooClass()
bar(**f)
and have it output 3
This is the most related question I could find but it doesn't address this so I'm thinking it might not be possible.
Currently what my solution would be this:
class FooClass:
def __init__(self):
self.a = a
self.b = b
def to_dict(self):
return {
"a" : self.a,
"b" : self.b
}
f = FooClass()
bar(**f.to_dict())
As pointed out in the comments, writing a conformant subclass of the collections.abc.Mapping abstract class is the way to go. To (concretely) subclass this class, you need to implement __getitem__, __len__, and __iter__ to behave consistently like a dictionary would. So that means __getitem__ expects a string, __iter__ returns an iterable of strings, etc.
For a simple example, we'll simply delegate all of these to self.__dict__, but in real code you'd likely want to do something more refined.
from collections.abc import Mapping
class FooClass(Mapping):
def __init__(self, a, b):
self.a = a
self.b = b
def __getitem__(self, x):
return self.__dict__[x]
def __iter__(self):
return iter(self.__dict__)
def __len__(self):
return len(self.__dict__)
def bar(a, b):
return a + b
foo = FooClass(40, 2)
print(bar(**foo))
Aside from reyling on vars(f) or f.__dict__, you could use a dataclass.
from dataclasses import dataclass, asdict
#dataclass
class FooClass:
a: int
b: int
Demo:
>>> f = FooClass(1, 2)
>>> asdict(f)
{'a': 1, 'b': 2}
def bar(a, b):
return a + b
class FooClass:
def __init__(self, a, b):
self.a = a
self.b = b
f = FooClass(1, 2)
print(bar(*f.__dict__.values()))
# print(bar(**f.__dict__)) # Also works
Output:
3
Are there Python versions that allow defining your class like this:
class Foo:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
and then adding class attributes, such as BAR_1, BAR_2, etc.:
class Foo:
BAR_1 = ...
BAR_2 = ...
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
which are actually "special cases of Foo", such as:
class Foo:
BAR_1 = Foo(4, 9, 16)
BAR_2 = Foo(2, 3, 5)
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
so that, in my code, I can either make my own Foos or get common, predefined Foos by working directly with Foo.BAR_1 and Foo.BAR_2?
The code above obviously does not work, otherwise I would not post the question (Foo is an unresolved reference when defining BAR_1 and BAR_2). I found a trick on SO how to sort-of achieve this -> defining a custom ClassProperty class:
class ClassProperty(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
which then allows me to define Foo as
class Foo:
#ClassProperty
def BAR_1(cls):
return Foo(4, 9, 16)
#ClassProperty
def BAR_2(cls):
return Foo(2, 3, 5)
...
and that works, but the issue is that Foo.__init__ is called everytime whenever Foo.BAR_1 or Foo.BAR_2 is retrieved, which can be useful in certain situations (precisely those where you always want separate instances), but in the special case where Foo is simply a messenger class which is coincidentally hard to load (like a result of a computation for example), this solution is unfeasible. I'd like for the constructor of Foo to be called exactly once for BAR_1, exactly once for BAR_2 (ideally lazily, during the first retrieval, that would be fantastic), and after that it would only return the created instances. So, is there a way to do this?
I use Python 3.8.6.
During the time I composed the question body, I figured out I can just define Foo like this:
class Foo:
#ClassProperty
def BAR_1(cls):
if not hasattr(cls, '_Foo__BAR_1'):
cls.__BAR_1 = Foo(4, 9, 16)
return cls.__BAR_1
#ClassProperty
def BAR_2(cls):
if not hasattr(cls, '_Foo__BAR_2'):
cls.__BAR_2 = Foo(2, 3, 5)
return cls.__BAR_2
Now, I can call Foo.BAR_X for retrieval of a defined Foo.__BAR_X which is actually an instance of Foo, which is always created only once.
I want to create a class with attributes that can be __setattr__-ed by its methods internally, so an attempt like self.attr = value would raise an AttributeError. This is what I have so far:
class MyClass():
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def __repr__(self):
return '%r class with a=%s, b=%s, c=%s' % (self, self.a, self.b, self.c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
self.attr = value
>>> obj = MyClass(1,2,3)
>>> obj.setattr_(a,4) # obj.a = 4
AttributeError: 'obj' is read-only # __setattr__ method also applies internally
This is a use case for properties. Properties without a setter are read-only. In the following, a and b are read-only, while c is not.
class MyClass:
def __init__(self, a, b, c):
self._a = a
self.b = b
self._c = c
# a is a read-only property
#property
def a(self):
return self._a
# b is an ordinary attribute
# c is a property you can set
#property
def c(self):
return self._c
#c.setter
def c(self, value):
self._c = value
Since you have defined only getters for the a, attempts to
change its value will fail. Attempts to change b will succeed as expected. Attempts to change c will succeed as
if it were a regular attribute.
>>> obj = MyClass(1,2,3)
>>> obj.a = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> obj.b = 5
>>> obj.c = 6
>>> obj.c
6
You can use properties in Python for this type of tasks. First, you make your attribute 'private' by adding two underscores, then you create a getter method with the #property decorator:
class MyClass:
def __init__(self, a, b, c):
self.__a, self.__b, self.__c = a, b, c
#property
def a(self):
return self.__a
#property
def b(self):
return self.__b
#property
def c(self):
return self.__c
Now, you can use your class like this:
>>> my_object = MyClass('foo', 'bar', 'bar')
>>> print(my_object.b)
bar
>>> my_object.b = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Note
I wrote 'private' because you can still access it if you really want:
>>> my_object._MyClass__b = 42
>>> print(my_object.b)
42
This has to do with the Zen of Python: "We’re all consenting adults here".
Please use the properties.
Anyway, it is good to understand the internals, here is a working code based on your question. Just to play with.
Once you redefine __setattr__ to fail, there is no way to set an attribute in that class. But there is still a working __setattr__ left in the parent class.
class MyClass():
def __init__(self, a, b, c):
self.setattr_('a', a)
self.setattr_('b', b)
self.setattr_('c', c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
super().__setattr__(attr, value)
obj = MyClass(1,2,3)
obj.setattr_('a',4) # note that a is a name (string)
I have a class A:
class A(object):
def pprint(x):
print(x)
Then I have a class B:
class B(object):
def pprint(x):
x += 1
# find a way to call A.pprint(x)
Then I have a child class:
class Child(B, A):
pass
Which should be used:
child = Child()
child.pprint(1)
>>> 2
I can make changes to B but not to A. I cannot refer to A directly in B. B will never be instantiated directly, always via children class.
After the explanation - what you need is not super() you need something like sibling_super() to find the next class in the multiple inheritance chain. You can poll Python's MRO for that, for example:
class A(object):
def pprint(self, x): # just to make it valid, assuming it is valid in the real code
print(x)
class B(object):
#staticmethod
def sibling_super(cls, instance):
mro = instance.__class__.mro()
return mro[mro.index(cls) + 1]
def pprint(self, x):
x += 1
self.sibling_super(B, self).pprint(self, x)
class Child(B, A):
pass
child = Child()
child.pprint(1) # 2
You have a couple of options for accessing the A method from the B class without having B inherit from A.
First, you could create a staticmethod and call it from B.
class A(object):
#staticmethod
def pprint(x):
print(x)
class B(object):
def pprint(self, x):
print(x + 1)
A.pprint(x)
Or you could inherit A in B like this:
class A(object):
def pprint(self, x):
print(x)
class B(A):
def pprint(self, x):
print(x + 1)
super(B, self).pprint(x)
Then for your Child class only inherit from B:
class Child(B):
pass
>>> c = Child()
>>> c.pprint(1)
2
1
OK, newest solution.
import inspect
class C(B, A):
def pprint(self, x):
a_class = inspect.getmro(Child)[-2]
a_class.pprint(self, x)
Since object will be the last result in inspect.getmro(Child) we skip that one to get the one before the last one, which is A. We then call that class's pprint method. You could also, to be more sure, if you know the __name__ of the class you want to call, iterate over the results from inspect.getmro(Child) and find the one that you want.
I have a class:
class A(object):
def __init__(self,a,b,c,d,e,f,g,...........,x,y,z)
#do some init stuff
And I have a subclass which needs one extra arg (the last W)
class B(A):
def __init__(self.a,b,c,d,e,f,g,...........,x,y,z,W)
A.__init__(self,a,b,c,d,e,f,g,...........,x,y,z)
self.__W=W
It seems dumb to write all this boiler-plate code, e.g passing all the args from B's Ctor to the inside call to A's ctor, since then every change to A's ctor must be applied to two other places in B's code.
I am guessing python has some idiom to handle such cases which I am unaware of. Can you point me in the right direction?
My best hunch, is to have a sort of Copy-Ctor for A and then change B's code into
class B(A):
def __init__(self,instanceOfA,W):
A.__copy_ctor__(self,instanceOfA)
self.__W=W
This would suit my needs since I always create the subclass when given an instance of the father class, Though I am not sure whether it's possible...
Considering that arguments could be passed either by name or by position, I'd code:
class B(A):
def __init__(self, *a, **k):
if 'W' in k:
w = k.pop('W')
else:
w = a.pop()
A.__init__(self, *a, **k)
self._W = w
Edit: based on Matt's suggestion, and to address gnibbler's concern re a positional-argument approach; you might check to make sure that the additional subclass-specific argument is being specified—similar to Alex's answer:
class B(A):
def __init__(self, *args, **kwargs):
try:
self._w = kwargs.pop('w')
except KeyError:
pass
super(B,self).__init__(*args, **kwargs)
>>> b = B(1,2,w=3)
>>> b.a
1
>>> b.b
2
>>> b._w
3
Original answer:
Same idea as Matt's answer, using super() instead.
Use super() to call superclass's __init__() method, then continue initialising the subclass:
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, w, *args):
super(B,self).__init__(*args)
self.w = w
In situations where some or all of the arguments passed to __init__ have default values, it can be useful to avoid repeating the __init__ method signature in subclasses.
In these cases, __init__ can pass any extra arguments to another method, which subclasses can override:
class A(object):
def __init__(self, a=1, b=2, c=3, d=4, *args, **kwargs):
self.a = a
self.b = b
# …
self._init_extra(*args, **kwargs)
def _init_extra(self):
"""
Subclasses can override this method to support extra
__init__ arguments.
"""
pass
class B(A):
def _init_extra(self, w):
self.w = w
Are you wanting something like this?
class A(object):
def __init__(self, a, b, c, d, e, f, g):
# do stuff
print a, d, g
class B(A):
def __init__(self, *args):
args = list(args)
self.__W = args.pop()
A.__init__(self, *args)