Chaining instance methods in python - python

I would like create a class in python with method and sub-method.
Example what I want to do :
foo = Foo()
foo.playlist('my playlist').add('i_add_a_track_in_"my playlist".ogg')
foo.playlist('my playlist').delete('i_remove_this_track.ogg')
I have this code for now :
class Foo(object):
def playlist(self, name):
pass #my function...
def add(self, track):
pass #adding track
def delete(self, track):
pass #delete track
Please help me, I don't know how i can do it.
Thank you

IIUC, you want to chain method calls one after another? All you'd have to do is return self at the end of each function.
class Foo(object):
...
def playlist(self, name):
...
return self
... # and so on
MVCE:
In [229]: class Foo:
...: def __init__(self, data):
...: self.data = data
...:
...: def add(self, val):
...: self.data += val
...: return self
...:
...: def sub(self, val):
...: self.data -= val
...: return self
...:
In [231]: x = Foo(0)
In [232]: x = x.add(10).sub(5) # or just x.add(10).sub(5)
In [233]: x.data
Out[233]: 5

If I understand correctly, foo.playlist('someplaylist').do_something() should actually be a shortcut for
playlist = foo('someplaylist')
playlist.do_something()
where playlist is NOT a foo object (ie: foo.do_something() is not supposed to make any sense and should just raise an error) but an instance of a distinct class.
If that's indeed the case, you actually want two classes: Foo with method playlist(...) that returns a Playlist object, and Playlist with add() and delete() methods:
class Playlist(object):
def __init__(self, name):
self.name = name
def add(self, what):
print("adding {} to playlist {}".format(what, self.name))
def delete(self, what):
print("deleting {} from playlist {}".format(what, self.name))
class Foo(object):
def playlist(self, name):
return Playlist(name)

Related

multiple functions in Python

We have some variable, or other instance: a='?'.
We have such input:
f = a(3112).bas(443).ssad(34)
When we type
print(f)
Output should be:
3112a-443bas-34ssad
I've tried some ways to solve this and have found information about chaining, but I still have the problem. I can't return class name to the brginning of the string.
This, what I have:
class A():
def __getattribute__(self, item):
print (str(item))
return super(A, self).__getattribute__(item)
def __init__(self, x):
self.x = x
print (str(x))
def b(self, item):
print (str(item))
return self
def c(self, item):
print (str(item))
return self
def d(self, item):
print (str(item))
return self
A(100).b(200).c(300).d(400)
My output:
100
b
200
c
300
d
400
But I couldn't concatenate it in one string.
Dynamic way
class A(object):
def __init__(self, integer):
self._strings = ['{}a'.format(integer)]
def __getattr__(self, attrname, *args):
def wrapper(*args, **kwargs):
self._strings.append('{}{}'.format(args[0], attrname))
return self
return wrapper
def __str__(self):
return '-'.join(self._strings)
print(A(100).bas(200).ssad(300))
Output
100a-200bas-300ssad
But also
print(A(100).egg(200).bacon(300).SPAM(1000))
Output
100a-200egg-300bacon-1000SPAM
Static way
class A(object):
def __init__(self, integer):
self._strings = ['{}a'.format(integer)]
def bas(self, integer):
self._strings.append('{}bas'.format(integer))
return self
def ssad(self, integer):
self._strings.append('{}ssad'.format(integer))
return self
def __str__(self):
return '-'.join(self._strings)
print(A(100).b(200).c(300))
Output
100a-200bas-300ssad
More about __str__
You can override the __str__ method to define your specific output:
class A():
def __init__(self, a, b="", c="", d=""):
self._a = a
self._b = b
self._c = c
self._d = d
def __str__(self):
return '{}a-{}b-{}c-{}d'.format( self.a, self.b, self.c, self.d )
def b(self, item):
self._b = item
return self
def c(self, item):
self._c = item
return self
def d(self, item):
self._d = item
return self
f = A(100).b(200).c(300).d(400)
print(f) # 100a-200b-300c-400d
Here I tried it in another way , ie, If you want to take the function name instead of manually giving it you can use inspect in python. Try this code :
import inspect
class A():
l = []
def __init__(self, x):
self.x = x
print (str(x))
self.l.append(str(x) + "a")
def b(self, item):
print (str(item))
self.l.append(str(item) + inspect.stack()[0][3])
return self
def c(self, item):
print (str(item))
self.l.append(str(item) + inspect.stack()[0][3])
return self
def d(self, item):
print (str(item))
self.l.append(str(item) + inspect.stack()[0][3])
return self
print("-".join(A(100).b(200).c(300).d(400).l))
The o/p is like :
'100a-200b-300c-400d'

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)

Automatically create (and keep) an object when accessed

I would like to do something like this:
class A:
def hello(): print "Hello"
# I do not want to explicitly setup a:
a = A()
# a = A() -> I want this to happen automatically when I access a
# My first try is this:
def a():
return A()
# Also, I do not want to call a as a function a(): it must be an object
# And it must stay alive and initialized
a.hello() # a is created, as object of class A
a.hello() # I do not want a second instantiation
How can I implement this? properties? cached-properties? They are only for classes: a is a module-level object.
Maybe something like this:
class A(object):
def hello(self):
print "Hello"
class LazyA(object):
def __init__(self):
self.instance = None
def __getattr__(self, k):
if self.instance is None:
self.instance = A()
return getattr(self.instance, k)
a = LazyA()
def lazyinit(cls):
class p(object):
def __init__(self, *args, **kws):
self._init = lambda: cls(*args, **kws)
self._obj = None
def __getattr__(self, k):
if not self._obj:
self._obj = self._init()
return getattr(self._obj, k)
return p
Example:
#lazyinit
class A(object):
def __init__(self, a, b):
print("initializing...")
self.x = a + b + 2
def foo(self):
return self.x
x = A(39, 1)
print x
print x.foo()
print x.foo()
Generalization of the answer by Pavel:
class LazyClass(object):
def __init__(self, myclass, *args, **kwargs):
self.instance = None
self.myclass = myclass
self.args = args
self.kwargs = kwargs
def __getattr__(self, k):
if self.instance is None:
self.instance = self.myclass(*self.args, **self.kwargs)
return getattr(self.instance, k)
class A(object):
def __init__ (self, name):
self.name = name
print "Created"
def hello(self):
print "Hello " + self.name
import unittest
class TestLazyClass(unittest.TestCase):
def setUp(self):
self.a = LazyClass(A, 'Daniel')
def test_it(self):
self.a.hello()
self.a.hello()

Decorate a class in Python by defining the decorator as a class

What is a simple example of decorating a class by defining the decorator as a class?
I'm trying to achieve what has been implemented in Python 2.6 using PEP 3129 except using classes not functions as Bruce Eckel explains here.
The following works:
class Decorator(object):
def __init__(self, arg):
self.arg = arg
def __call__(self, cls):
def wrappedClass(*args):
return cls(*args)
return type("TestClass", (cls,), dict(newMethod=self.newMethod, classattr=self.arg))
def newMethod(self, value):
return value * 2
#Decorator("decorated class")
class TestClass(object):
def __init__(self):
self.name = "TestClass"
print "init %s"%self.name
def TestMethodInTestClass(self):
print "test method in test class"
def newMethod(self, value):
return value * 3
Except, in the above, wrappedClass is not a class, but a function manipulated to return a class type. I would like to write the same callable as follows:
def __call__(self, cls):
class wrappedClass(cls):
def __init__(self):
... some code here ...
return wrappedClass
How would this be done?
I'm not entirely sure what goes into """... some code here ..."""
If you want to overwrite new_method(), just do it:
class Decorator(object):
def __init__(self, arg):
self.arg = arg
def __call__(self, cls):
class Wrapped(cls):
classattr = self.arg
def new_method(self, value):
return value * 2
return Wrapped
#Decorator("decorated class")
class TestClass(object):
def new_method(self, value):
return value * 3
If you don't want to alter __init__(), you don't need to overwrite it.
After this, the class NormalClass becomes a ClassWrapper instance:
def decorator(decor_arg):
class ClassWrapper:
def __init__(self, cls):
self.other_class = cls
def __call__(self,*cls_ars):
other = self.other_class(*cls_ars)
other.field += decor_arg
return other
return ClassWrapper
#decorator(" is now decorated.")
class NormalClass:
def __init__(self, name):
self.field = name
def __repr__(self):
return str(self.field)
Test:
if __name__ == "__main__":
A = NormalClass('A');
B = NormalClass('B');
print A
print B
print NormalClass.__class__
Output:
A is now decorated. <br>
B is now decorated. <br>
\__main__.classWrapper

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