Magical method __len__() - python

How to call the __len__() function using an object of the class ?
class foo(object):
def __init__(self,data)
self.data = data
def __len__(self):
return len(self.data)
x = foo([1,2,3,4])

The idea behind a magic method is to be able to call it as x.__len__() or len(x). They don't return the output until explicitly called or have or stored in class variables.
Method 1: Call function explicitly
You can simply call the function explicitly as -
class foo(object):
def __init__(self,data):
self.data = data
def __len__(self):
print('i am in a magic function')
return len(self.data)
x = foo([1,2,3,4])
len(x) #or x.__len__() which is equivalent
i am in a magic function
4
Method 2: Display during initialization
Or if you want to trigger it during initialization, just add it in the __init__(). Remember, init wouldn't return anything so you can push the output into stdio with a print.
class foo(object):
def __init__(self,data):
self.data = data
print(self.__len__())
def __len__(self):
print('i am in a magic function')
return len(self.data)
x = foo([1,2,3,4])
i am in a magic function
4
Method 3: Store and access as a class variable
If you want to save it, then you can define a self.length variable which can store it and can be retrieved by x.length
class foo(object):
def __init__(self,data):
self.data = data
self.length = self.__len__()
def __len__(self):
return len(self.data)
x = foo([1,2,3,4])
x.length
4

You can do it this way:
>>>x = foo([1,2,3,4])
>>>len(x)
4

It basically allows you to use len().
Imagine you have only:
class foo(object):
def __init__(self,data):
self.data = data
x = foo([1,2,3,4])
print(len(x))
Now, if you have:
class foo(object):
def __init__(self,data):
self.data = data
self.length = len(data)
x = foo([1,2,3,4])
print(len(x))
You still have the error:
(You can get the length with x.length though)
But if you add the magical method __len__():
class foo(object):
def __init__(self,data):
self.data = data
self.length = len(data)
def __len__(self):
return self.length
x = foo([1,2,3,4])
print(len(x))
You now use len() successfully.

Same way you call any other function. By its name.
print(x.__len__())
which will give 4 for your code

If we go with your class called foo() we can call the method __len__ like this.
a = foo([1,2,3,4])
b = a.__len__()
Or if you want to save the length within the class:
class foo(object):
def __init__(self,data)
self.data = data
self.len = None
def __len__(self):
self.len = len(self.data)
a = foo([1,2,3,4])
a.__len__()
print(a.len)

Related

python object building using multi-inheritance

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

How do change class attributes individually?

I read a bit about getters and setters, but haven't quite figured it out. One of my issues is the declaration in the init method: how can I change only one attribute if the method needs to arguments? Any other methods are of course welcome as well.
class States:
def __init__(self, f=0, n=0):
self.state = n
self.level_state = f
#property
def state(self, n):
return self._state
#state.setter
def state(self, n):
self._state = n
#property
def level_state(self, f):
return self._level_state
#level_state.setter
def state(self, f):
self._level_state = f
Example situation, changing the attributes individually:
Situation1:
States().state = 3
Situation2:
States().level_state = 2
The code doesn't work because you've named the underlying attributes the same as the corresponding properties.
You need to use the same attribute names as the property getters/setters are referring to:
class States:
def __init__(self, f=0, n=0):
self._state = n # <-- changed here
self._level_state = f # <-- changed here
#property
def state(self, n):
return self._state
#state.setter
def state(self, n):
self._state = n
#property
def level_state(self, f):
return self._level_state
#level_state.setter
def state(self, f):
self._level_state = f
But the simple solution for this case would be to not use properties at all:
class States:
def __init__(self, f=0, n=0):
self.state = n
self.level_state = f
# done

Chaining instance methods in 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)

how to pass method name as a parameter in python class

This is my code, my intention is to pass the method name as a parameter when I initialize the object and I want to run the method 'num' (second argument) of times. Basically get n number of results (as mentioned in 2nd argument).
class Foo(object):
faker = Faker()
def __init__(self, custom_method, num=1):
self.values = []
self.custom_method = custom_method
self.num = num
for x in self.num:
self.custom_method = self.values.append(custom_method)
def random_first_name(self):
self.custom_method = self.faker.first.name()
return self.custom_method
def random_phone(self):
self.custom_method = self.faker.random.phone()
return self.custom_method
b = Foo(random_first_name, 1)
c = Foo(random_phone,2)
I guess that you may want to use the function getattr.
class Foo(object):
faker = Faker()
def __init__(self, custom_method, num=1):
self.custom_method = custom_method
self.num = num
#property # Briefly, the property decorator makes the job of calling the callable for you. I.e. There is no need to do self.method(), self.method is enough.
def random_first_name(self):
return self.faker.first.name()
#property
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self):
return [getattr(self, self.custom_method)\
for _ in range(self.num)]
I cannot instantiate this class, but this could be used as follows:
>>> foo1 = Foo('random_first_name', 1)
>>> foo1.call_method_num_times()
['John']
>>> foo2 = Foo('random_phone', 2)
>>> foo2.call_method_num_times()
['0123456789', '9876543210']
To (even more) reorganize your class in a (subjectively) better fashion, I would do
class Foo(object):
def __init__(self):
self.faker = Faker()
#property
def random_first_name(self):
return self.faker.first.name()
#property
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self, custom_method, num=1):
return [getattr(self, custom_method)\
for _ in range(num)]
Thus allowing you for instantiating Foo only once
>>> foo = Foo()
>>> foo.call_method_num_times('random_first_name')
['John']
>>> foo.call_method_num_times('random_phone', 2)
['0123456789', '9876543210']
If you are not comfortable with the use of the python native property descriptor, you can keep your two methods as explicite ones. In this case, you would define the class Foo as follows
class Foo(object):
def __init__(self):
self.faker = Faker()
def random_first_name(self):
return self.faker.first.name()
def random_phone(self):
return self.faker.random.phone()
def call_method_num_times(self, custom_method, num=1):
return [getattr(self, custom_method)()\
for _ in range(num)]
Which would change nothing in ways of using Foo
>>> foo = Foo()
>>> foo.call_method_num_times('random_first_name')
['John']
>>> foo.call_method_num_times('random_phone', 2)
['0123456789', '9876543210']

Python Class Objects Attribute Referencing

I have two classes. a and b.
In one of class a's methods, I created an object of class b. One of class b attributes takes a function. So say I gave it a random function but does this function of class b have access to class a's attribute? even though I didn't pass it in directly as a parameter?
class b:
def __init__(self):
self.attribute_function = None
class a:
def __init__(self):
self.temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda self: self.counter < self.temp
return obj.attribute_function()
if __name__ == "__main__":
#pass
obj = a()
print obj.temp()
In the above example, I tried to provide a really basic example, but if you run it, it doesn't work...
Revised Code, class a should look like this:
class a:
def __init__(self):
self.temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda args: self.counter < self.temp
return obj.attribute_function(1) # i added this 1 to fill in arg
This works:
class b:
def __init__(self):
self.attribute_function = None
class a:
def __init__(self):
self._temp = 10
self.counter = 0
def temp(self):
obj = b()
obj.attribute_function = lambda self=self: self.counter < self._temp
return obj.attribute_function()
if __name__ == "__main__":
obj = a()
print obj.temp()
On problem you had is self.temp = 10 which shadowed your method temp().
Another problem: lambda self: self.counter < self._temp. Your lambda function was expecting an argument. But omitting self is not a good idea lambda : self.counter < self._temp, because if you call obj.attribute_function() somewhere where self is not available or has changed - it will not find self or use another self. self=self fixes that.
But generally such magic is an anti-pattern. Tell us what are your trying to achieve, and there should be a better way to do what you want. Otherwise this kind of code will ensure many headaches.
I think this is a better solution (called strategy pattern):
class B:
def __init__(self, a):
self.a = a
def temp(self):
return self.a.temp()
class A:
def __init__(self):
self._temp = 10
self.counter = 0
def temp(self):
return self.counter < self._temp
if __name__ == "__main__":
obj = B(A())
print obj.temp()
Your example does not work because you have a name collision at temp
You have assigned temp to be both a method:
def temp(self):
and an attribute:
self.temp = 10

Categories