Calling classmethod multiple times in python - python-3.x

I am trying to create a classmethod which can be called again and again, however it only works once and stops. Here is the code:
class NewBytes(bytes):
def __init__(self, var):
self.var= var
#classmethod
def rip(cls):
return cls(var[2:])
a = b"12asd5789"
x = NewBytes(a)
print(x, x.rip(), x.rip().rip(), x.rip().rip().rip())
Here is what I got from this:
b'12asd5789' b'asd5789' b'asd5789' b'asd5789'
However, what I want to have is:
b'12asd5789' b'asd5789' b'd5789' b'789'
Thanks in advance.

Probably you don't actually want a class method, since you need access to instance state here.
class NewBytes(bytes):
def __init__(self, x):
self.x = x
def rip(self):
return type(self)(self.x[2:])

My previous answer of using self.x doesnt make sense since this is a class method (too quick to answer). I think this is a case of the XY problem, see the below example of how to use a class method.
class Test(object):
x = "hey there whats up this is a long string"
#classmethod
def TestFunction(cls):
cls.x = cls.x[3:]
print(cls.x)
print(Test().x)
Test().TestFunction()
Test().TestFunction()
Test().TestFunction()
Test().TestFunction()
Test().TestFunction()

Related

Is it posible to use super() with all the parents in Python3 multiple inheritance?

If a class has 2 or more parents, how can I use super(), or any equivalent, to make reference to each of them? For example here:
class A:
def __init__(self, x): self.a = x
class B:
def __init__(self, y): self.b = y
class C(A,B):
def __init__(self, x, y):
super().__init__(x)
B.__init__(self,y) # I would to like to use super() here too
ObjetoC = C(4,3);
print (ObjetoC.a, ObjetoC.b) # It works fine
PD: I understand the MROrder. I just wonder if there is a way to reach a non-priority parent with super() or equivalent. Or if there is another elegant way to do that I have already done by using B.__init__(self,y)

Multiple inheritance problem with super()

I'm having a problem with multiple inheritance that I can't seem to figure out. Here is a very abstracted minimal example that reproduces my error (my code is much more complex than this).
class Thing(object):
def __init__(self, x=None):
self.x = x
class Mixin(object):
def __init__(self):
self.numbers = [1,2,3]
def children(self):
return [super().__init__(x=num) for num in self.numbers]
class CompositeThing(Mixin, Thing):
def __init__(self):
super().__init__()
def test(self):
for child in self.children():
print(child.x)
obj = CompositeThing()
obj.test()
Per this, I expect the children() method to return a list of Things built up from self.numbers. Instead, I get TypeError: super(type, obj): obj must be an instance or subtype of type. Incidentally, the same thing happens if I don't call the constructor and allow children to return super() 3 times (i.e., the uninstantiated superclass). Any ideas why this might be happening?
Thanks in advance!
In line 9 of your code, it looks like you are trying to call __init__ of object. I am assuming you meant to have Mixin inherit from Thing.
class Thing(object):
def __init__(self, x=None):
self.x = x
class Mixin(Thing):
def __init__(self):
self.numbers = [1,2,3]
def children(self):
return [super().__init__(x=num) for num in self.numbers] # Now calls Thing.__init__ instead of object.__init__
class CompositeThing(Mixin, Thing):
def __init__(self):
super().__init__()
def test(self):
for child in self.children():
print(child.x)
obj = CompositeThing()
obj.test()
Actually, I figured it out. There were two problems: (1) super() doesn't work as expected inside comprehensions because comprehensions in Py3 have their own scope - this was causing the TypeError I was experiencing. (2) What I was really trying to do was create a new instance of the parent, rather than calling a method from the parent. I have posted a new question for just the latter problem for clarity.

Python subclass that takes superclass as argument on instantiation?

I am trying to create a wrapper class in Python with the following behaviour:
It should take as an argument an existing class from which it should inherit all methods and attributes
The wrapper class methods should be able to use Python super() to access methods of the superclass (the one passed as an argument)
Because of my second requirement I think the solution here will not suffice (and in any case I am having separate issues deepcopying some of the methods of the superclass' I am trying to inherit from).
I tried this but it's not correct...
class A:
def shout(self):
print("I AM A!")
class B:
def shout(self):
print("My name is B!")
class wrapper:
def __init__(self, super_class):
## Some inheritance thing here ##
# I initially tried this but no success...
super(super_class).__init__() # or similar?
def shout(self):
print('This is a wrapper')
super().shout()
And this is the behaviour I require...
my_wrapper = wrapper(A)
my_wrapper.shout()
# Expected output:
# > This is a wrapper
# > I AM A
my_wrapper = wrapper(B)
my_wrapper.shout()
# Expected output:
# > This is a wrapper
# > My name is B!
Is inheritance the correct approach here, if so am I sniffing in the right direction? Any help is appreciated, thanks :)
Edit for context:
I intend to build multiple wrappers so that all of my ML models have the same API. Generally, models from the same package (sklearn for example) have the same API and should be able to be wrapped by the same wrapper. In doing this I wish to modify/add functionality to the existing methods in these models whilst keeping the same method name.
If wrapper has to be a class then a composition solution would fit much better here.
Keep in mind that I turned the shout methods to staticmethod because in your example you pass the class to wrapper.shout, not an instance.
class A:
#staticmethod
def shout():
print("I AM A!")
class B:
#staticmethod
def shout():
print("My name is B!")
class wrapper:
def __init__(self, super_class):
self._super_class = super_class
def __getattr__(self, item):
try:
return self.__dict__[item].__func__
except KeyError:
return self._super_class.__dict__[item].__func__
def a_wrapper_method(self):
print('a wrapper attribute can still be used')
my_wrapper = wrapper(A)
my_wrapper.shout()
my_wrapper = wrapper(B)
my_wrapper.shout()
my_wrapper.a_wrapper_method()
Outputs
This is a wrapper
I AM A!
This is a wrapper
My name is B!
a wrapper attribute can still be used
So I went for a function in the end. My final solution:
class A:
def shout(self):
print("I AM A!")
class B:
def shout(self):
print("My name is B!")
def wrap_letter_class(to_wrap):
global letterWrapper
class letterWrapper(to_wrap):
def __init__(self):
super().__init__()
def shout(self):
print('This is a wrapper')
super().shout()
def __getstate__(self):
# Add the wrapper to global scope before pickling
global letterWrapper
letterWrapper = self.__class__
return self.__dict__
return letterWrapper()
Which produces the desired behaviour...
In [2]: wrapped = wrap_letter_class(A)
In [3]: wrapped.shout()
This is a wrapper
I AM A!
In [4]: wrapped = wrap_letter_class(B)
In [5]: wrapped.shout()
This is a wrapper
My name is B!
Something not mentioned in my initial question was that I intended to pickle my custom class, this is not possible if the class is not defined in the global scope, hence the __getstate__ and global additions.
Thanks!

super().method() vs Parent.method()

class Parent():
def __init__(self, x):
self.x = x
print('init parent')
def hithere(self):
print('hey there')
print(self.x)
class Child(Parent):
def __init__(self, x):
self.x = x
super().hithere()
child = Child(3)
Here I have a Parent class and a child class which inherets from the parent.
Why do I need super() If I can always do the same thing by replacing it with the name of the parent class im inhereting:
class Parent():
def __init__(self, x,y):
self.x = x
self.y = y
print('init parent')
def hithere(self):
print('hey there')
print(self.x)
class Child(Parent):
def __init__(self, x):
self.x = x
Parent.hithere(self)
child = Child(3)
which does the same thing.
My second question is it correct to say that abstract classes can't have atributes? If Parent was an abstract class, then whenever one of it's methods calls for self like hithere(self), I need to pass it back with super().method(self). So those attributes are actually Child's attributes, which just so happen to have the same attribut names as the parent class to be used.
This is one and the same thing, but you should you super() because:
In future, if you want to change the name of the parent class, then you don't have to change the name for every instance.
For e.g.
Parent.hithere(self)
Parent.hithere1(self)
Parent.hithere2(self)
Now if you change the name of your parent class, then you would have to change the name Parent for every instance. This would not be the case, if you would do:
super().hithere1()
super().hithere2()
super().hithere3()
I think your second question is ambiguous, but you can read more about the abstract class here.
You almost always want to use self.foo(), unless you're writing a Child.foo method that overwrites Parent.foo.
If you later write a Child.hithere method, your current code won't use it, which is frequently not what you want.

Return a value of class rather than the class itself Python when called

As I am playing around with classes in Python, I thought the following might be useful. Given:
class NewClass:
def __init__self():
self.value = 1
new = NewClass()
Is there a way that by calling new, it would return the new.value of 1, rather than the class itself
<__main__.NewClass object at 0x7faa096d38d0>?
I assume it might not be possible and people are against it, but I figured I'd ask anyway.
class NewClass():
def __init__(self):
pass
def Ret(self):
self.value = 1
return self.value
new = NewClass().Ret()
print(new)

Resources