one function as a class and also instance method - python

This could be a so called XY problem, so let me start with what I want:
ed = Edit.add(1).add(2).add(3)
print(ed.op) # [1,2,3]
Following is what I tried and basically I got working code, but I'm not sure if I did it right
or if there are other options.
The first add(1) is a class method call and the following add(2) and add(3) are instance method calls. I started here:
class Edit:
def __init__(self):
self.op = []
#classmethod
def add(cls, x):
self = cls()
return self.add_im(x)
def add_im(self, x): # im = instance method
self.op.append(x)
return self
ed = Edit.add(1).add_im(2).add_im(3)
print(ed.op)
but I wanted the same name everywhere, so I added some attribute name rewriting:
class Edit:
def __init__(self):
self.op = []
def __getattribute__(self, name):
if name == "add":
name = "add_im"
return super().__getattribute__(name)
#classmethod
def add(cls, x):
self = cls()
return self.add(x)
# im = instance method
def add_im(self, x):
self.op.append(x)
return self
ed = Edit.add(1).add(2).add(3)
print(ed.op)
UPDATE:
as #lllrnr101 noted in the comments, one part of my question has been answered here: Same name for classmethod and instancemethod
My question is broader, I think it does not have to be closed as a duplicate.
UPDATE2:
After studying the mentioned thread I have now new version I am satisfied with:
class dualmethod(classmethod):
def __get__(self, instance, typ):
if instance is None:
instance = typ() # create an instance "on the fly"
return self.__func__.__get__(instance, typ)
class Edit:
def __init__(self):
self.op = []
#dualmethod
def add(self, x):
self.op.append(x)
return self
ed = Edit.add(1).add(2).add(3)
print(ed.op) # [1,2,3]

Related

Parameter self unfilled, but when I fill it it gives expected type ' ' but got 'Type[]' warning

class Player:
def __init__(self):
self.godHole = GodHole
self.pits = []
for i in range(0, 6):
self.pits.append(Pit())
def return_stones(self):
return self.godHole.return_stones(self.godHole)
#or return self.godHole.return_stones()
# same warning for invoking seld.godHole.increment_stones()
class GodHole:
def __init__(self):
self.stones = 0
def return_stones(self):
return self.stones
def change_stones(self, s):
self.stones = s
def increment_stones(self):
self.stones += 1
When using the commented line I get self unfilled warning. when I used the first one I get the expected type ' ' but got 'Type[]' warning
What am I doing wrong? How can I fill self parameter? Can I even access instance attributes this way?
The attribute godHole of class Player, it is just a class, and no instantiation operation is performed, you directly use the instance method return_stones below, and pass in the class GodHole, which is wrong.
There are two ways to execute instance methods:
call directly using the class instance
when a class uses an instance method, the instance is passed in as a parameter
class Player:
def __init__(self):
self.godHole = GodHole()
self.pits = []
for i in range(0, 6):
self.pits.append(Pit())
def return_stones(self):
return self.godHole.return_stones()
or
class Player:
def __init__(self):
self.godHole = GodHole
self.pits = []
for i in range(0, 6):
self.pits.append(Pit())
# def return_stones(self):
# return self.godHole.return_stones(self.godHole())
def return_stones(self, obj: GodHole):
# obj is an instance object of class GodHole
return self.godHole.return_stones(obj)

Decorators on classmethods

I have child classes which inherit some basic functionality from a parent class.
The child classes shall have a generic constructor prepare_and_connect_constructor() which does some magic around the object creation of the parent class.
For simplicity, the magic is done by a simple function based decorator (ultimately, it should be a part of the parent class).
def decorate_with_some_magic(func):
def prepare_and_connect(*args, **kwargs):
print("prepare something")
print("create an object")
obj = func(*args, **kwargs)
print("connect obj to something")
return obj
return prepare_and_connect
class Parent:
def __init__(self, a):
self.a = a
def __repr__(self):
return f"{self.a}"
class Child(Parent):
#classmethod
#decorate_with_some_magic
def prepare_and_connect_constructor(cls, a, b):
""" use the generic connection decorator right on object creation """
obj = super().__init__(a)
# put some more specific attributes (over the parents class)
obj.b = b
return obj
def __init__(self, a, b):
""" init without connecting """
super().__init__(a)
self.b = b
def __repr__(self):
return f"{self.a}, {self.b}"
if __name__ == '__main__':
print(Child.prepare_and_connect_constructor("special child", "needs some help"))
Using this code i finally get
obj = super().__init__(a)
TypeError: __init__() missing 1 required positional argument: 'a'
when running prepare_and_connect_constructor().
Actually I would expect that the super.__init__(a) call should be the same as in Child.__init__.
I guess the reason is related to the classmethod but I can't figure it out.
What's wrong with this call?
Update: In general what was wrong is that __init__ doesn't return an object.
Due to the hints and thoughts from the answers i modified my code to achieve what i need:
class Parent:
def __init__(self, a):
self.a = a
#staticmethod
def decorate_with_some_magic(func):
def prepare_and_connect(*args, **kwargs):
print("prepare something")
print("create an object")
obj = func(*args, **kwargs)
print("connect obj to something")
return obj
return prepare_and_connect
def __repr__(self):
return f"{self.a}"
class ChildWithOneName(Parent):
#classmethod
#Parent.decorate_with_some_magic
def prepare_and_connect_constructor(cls, a, b):
""" use the generic connection decorator right on object creation """
obj = super().__new__(cls)
obj.__init__(a, b)
print("Does the same as in it's __init__ method")
return obj
def __init__(self, a, b):
""" init without connecting """
super().__init__(a)
self.b = b
def __repr__(self):
return f"{self.a}, {self.b}"
class GodChild(Parent):
#classmethod
#Parent.decorate_with_some_magic
def prepare_and_connect_constructor(cls, a, names):
""" use the generic connection decorator right on object creation """
obj = super().__new__(cls)
obj.__init__(a, names)
# perform some more specific operations
obj.register_all_names(names)
print("And does some more stuff than in it's __init__ method")
return obj
def __init__(self, a, already_verified_names):
""" init without connecting """
super().__init__(a)
self.verified_names = already_verified_names
def register_all_names(self, names=[]):
self.verified_names = []
def verify(text):
return True
for name in names:
if verify(name):
self.verified_names.append(name)
def __repr__(self):
return f"{self.a}, {self.verified_names}"
if __name__ == '__main__':
print(ChildWithOneName.prepare_and_connect_constructor("special child", "needs some help"), end='\n\n')
print(GodChild.prepare_and_connect_constructor("unknown child", "needs some verification"), end='\n\n')
print(ChildWithOneName("my child", "is clean and doesn't need extra magic"))
decorate_with_some_magic is now a part of the Parent class (using a staticmethod) as it is a related generic functionality
Each child class (added one more for illustration) has it's own prepare_and_connect_constructor classmethod, which calls its own constructor and does optionally some additional work
You have a slight misunderstanding of the magic methods __init__ and __new__. __new__ creates a new object, e.g. returns a instance of the class. __init__ just modifies the object in place. So an easy fix for your problem would be de following:
#classmethod
#decorate_with_some_magic
def prepare_and_connect_constructor(cls, a, b):
""" use the generic connection decorator right on object creation """
obj = super().__new__(cls)
obj.__init__(a)
# put some more specific attributes (over the parents class)
obj.b = b
return obj
I however don't think you should use it like this. Instead, your probably should overwrite __new__
Decorators work on callables. Since there is no difference between calling a function and initiating a class, you can use your decorator directly on the class:
def decorate_with_some_magic(func):
def prepare_and_connect(*args, **kwargs):
print("prepare something")
print("create an object")
obj = func(*args, **kwargs)
print("connect obj to something")
return obj
return prepare_and_connect
class Parent:
#classmethod
def prepare_and_connect_constructor(cls, a, b):
return decorate_with_some_magic(cls)(a, b)
def __init__(self, a):
self.a = a
def __repr__(self):
return f"{self.a}"
class Child(Parent):
def __init__(self, a, b):
""" init without connecting """
super().__init__(a)
self.b = b
def __repr__(self):
return f"{self.a}, {self.b}"
if __name__ == '__main__':
normal_child = Child("normal child", "no help needed")
print(normal_child)
special_child = Child.prepare_and_connect_constructor("special child", "needs some help")
print(special_child)
Output:
normal child, no help needed
prepare something
create an object
connect obj to something
special child, needs some help

Can't get method name when using decorator

I suppose I'm missing something obvious, but I can't get the name of methods when I'm using decorators. When I run this code, I get the error:
AttributeError: 'str' object has no attribute "__name__"
Could somebody tell me how I can get the name of these decorated method?
Thanks
def Print(*arg, **kwarg):
func, *arguments = arg
print(func.__name__ + "(): {}".format(func=arguments[0]))
class Bob(object):
def __init__(self):
pass
#property
def stuff(self):
return "value from stuff property"
#stuff.setter
def stuff(self, noise):
return noise
class Tester:
def __init__(self):
self.dylan = Bob()
def randomTest(self):
Print(self.dylan.stuff, 1)
if __name__ == "__main__":
whatever = Tester()
whatever.randomTest()
stuff isn't a function or a method; it's a property. The syntax
#property
def stuff(...):
...
creates an instance of the property class using stuff as the argument to property, equivalent to
def stuff(...):
....
stuff = property(stuff)
and instances of property don't have a __name__ attribute, as you've seen.
(It's a little trickier with the setter, since the function and the property have to have the same name. But defining stuff a "second" time doesn't override the existing property named stuff.)
The individual methods are accessed via attributes of the property.
>>> Bob.stuff.fget.__name__
'stuff'
>>> Bob.stuff.fset.__name__
'stuff'
Note another, longer, way to create the same property:
class Bob:
def stuff_getter(self):
...
def stuff_setter(self, noise):
...
stuff = property(stuff_getter, stuff_setter)
del stuff_getter, stuff_setter # Clean up the namespace
def Print(*arg, **kwarg):
func, *arguments = arg
print(func.__name__ + "(): {}".format(func=arguments[0]))
class Bob():
def __init__(self, s):
self.stuff = s
#property
def myStuff(self):
return self.stuff
#myStuff.setter
def setStuff(self, noise):
self.stuff = noise
class Tester:
def __init__(self):
self.dylan = Bob(1)
def randomTest(self):
print(self.dylan.stuff)
if __name__ == "__main__":
whatever = Tester()
whatever.randomTest()
This should work :)

How to convert all elements automatically in a list to a given object in Python

I want to create a list child class that can convert all elements automatically in it to an object no matter the element is create by init or append or extend. So by using both for loop or getitem. Here's a simple example code. What kind of magic method should I use?
class A():
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Object A with name {}'.format(self.name)
class CustomerList(list):
def __init__(self, *args):
super(CustomerList, self).__init__(*args)
c = CustomerList('a')
c.append('b')
c[0] # Object A with name a
c[1] # Object A with name b
for ele in c:
print(c)
# Object A with name a
# Object A with name b
are you asking how to override __append__?
class A():
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Object A with name {}'.format(self.name)
class CustomerList(list):
def __init__(self, *args):
super(CustomerList, self).__init__(*args)
def append(self,letter):
super(CustomerList, self).append(A(letter))
I guess???.. but as mentioned in the comments if you want
my_custom_list.extend(["A","B","V"])
my_custom_list[2] = "A"
to work you will need to override
def __setitem__(self,key,value): # cover a[2]='A'
super(CustomerList,self).__setitem__(key,A(value))
def extend(self,other):
super(CustomerList,self).extend([A(val) for val in other])
of coarse you probably then need to override both __add__,__iadd__ at a minimum as well
I think what you're trying to do is: When you append a new item into the list, it is an object of class A. What you can do is override list.append function:
class A():
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Object A with name {}'.format(self.name)
class CustomerList(list):
def __init__(self, *args):
super(CustomerList, self).__init__(*args)
def append(self, arg):
new_obj = A(arg)
self.insert(len(self), new_obj)

Python Decorators and inheritance

Help a guy out. Can't seem to get a decorator to work with inheritance. Broke it down to the simplest little example in my scratch workspace. Still can't seem to get it working.
class bar(object):
def __init__(self):
self.val = 4
def setVal(self,x):
self.val = x
def decor(self, func):
def increment(self, x):
return func( self, x ) + self.val
return increment
class foo(bar):
def __init__(self):
bar.__init__(self)
#decor
def add(self, x):
return x
Oops, name "decor" is not defined.
Okay, how about #bar.decor? TypeError: unbound method "decor" must be called with a bar instance as first argument (got function instance instead)
Ok, how about #self.decor? Name "self" is not defined.
Ok, how about #foo.decor?! Name "foo" is not defined.
AaaaAAaAaaaarrrrgggg... What am I doing wrong?
Define decor as a static method and use the form #bar.decor:
class bar(object):
def __init__(self):
self.val = 4
def setVal(self,x):
self.val = x
#staticmethod
def decor(func):
def increment(self, x):
return func(self, x) + self.val
return increment
class foo(bar):
def __init__(self):
bar.__init__(self)
#bar.decor
def add(self, x):
return x
I know the question has been asked 11 years ago ...
I had the same problem, here is my solution to use an inherited private decorator :
class foo:
def __bar(func):
def wrapper(self):
print('beginning')
func(self)
print('end')
return wrapper
class baz(foo):
def __init__(self):
self.quux = 'middle'
#foo._foo__bar
def qux(self):
print(self.quux)
a = baz()
a.qux()
The output is :
beginning
middle
end

Categories