Is it possible to make a class object iterable in Python? [duplicate] - python

This question already has answers here:
Python: Make class iterable
(6 answers)
Closed 2 years ago.
I'm wondering if it's possible to make a class object iterable in Python (ie. a subclass of type, NOT an instance of a class).
I've tried the following code:
class Foo:
#classmethod
def __iter__(cls):
yield 1
print(next(Foo.__iter__())) # prints 1
print(next(iter(Foo))) # TypeError: 'type' object is not iterable

Turns out it's possible with metaclasses.
class Foo(type):
def __iter__(self):
yield self.baz
class Bar(metaclass=Foo):
baz = 1
print(type(Bar)) # prints "<class '__main__.Foo'>"
print(next(Bar.__iter__())) # prints "1"
print(next(iter(Bar))) # prints "1"
Thanks #DanielB for pointing me in the right direction.

This isn't possible, as it would imply that all type objects are iterable - as you stated, classes are of type type. The underlying reason for this is that the __iter__ method is looked up on the type, not the value. E.g., in:
for i in Foo:
print(i)
Python will look up type(Foo).__iter__, not Foo.__iter__.

Related

What type is "__main__.Example"? [duplicate]

This question already has answers here:
Understanding class type '__main__.ClassName'
(2 answers)
Closed 2 years ago.
I'm creating a class and checking the arguments for __init__.
class Example:
def __init__(self, a):
for key, val in locals().items():
print(type(val))
When I create this class, (something like Example(14)) I get <class '__main__.Example'> on the first line.
I know this is 'self', but what type is it?
The __main__ prefix is present because your class is defined in your script's global scope.
>>> class A:
... pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>>
The type of Example(14) is... well, Example, since that's the class of which that object is an instance.

How to make tuple subclass accept 2 arguments? [duplicate]

This question already has answers here:
Subclassing tuple with multiple __init__ arguments
(2 answers)
Closed 4 years ago.
I made a tuple subclass to add a property to a tuple. Using same logic as with a list subclass which works without problems.
Code:
class TupleObject(tuple):
def __init__(self, property, _tuple):
super().__init__(_tuple)
self.property = property
_tuple = TupleObject(property, (0, 0))
Throws error:
TypeError: tuple expected at most 1 arguments, got 2
How could I make this work? Using this exact method with a list subclass works as expected.
Because tuples are immutable, you need to override __new__ to be able to modify the object before the instance is created.
class TupleObject(tuple):
def __new__(cls, property, _tuple):
self = super().__new__(cls, _tuple)
self.property = property
return self
_tuple = TupleObject('a prop', (0, 0))
_tuple, _tuple.property
Produces
((0, 0), 'a prop')

Python, set class variable to int value not int type [duplicate]

This question already has answers here:
Should I, and how to, add methods to int in python?
(1 answer)
Can I add custom methods/attributes to built-in Python types?
(8 answers)
Closed 5 years ago.
If I have a class such as this
class foo():
def __init__(self, value = 0):
self.__value = value
def __set__(self, instance, value):
self.__value = value
def calc(self):
return self.__value * 3
def __repr__(self):
return str(self.__value)
I can now make a variable of the class foo and use it's functions.
n = foo(3)
print(n.calc())
No problems there but if I keep going with something like this.
n = 5
print(n.calc())
I will get an error, because I have now set n to an int object with the value 5 and thus does not have the calc() function.
I normally work with C++ so I'm confused because I thought that the __set__ function was supposed to override the = operator and then set __value to the value of 5 just like if I were to to use
operator=(int value)
In C++, I have looked for an explanation but have not found any.
All help appreciated.
As stated here.
The following methods only apply when an instance of the class
containing the method (a so-called descriptor class) appears in an
owner class (the descriptor must be in either the owner’s class
dictionary or in the class dictionary for one of its parents).

Checking if a class is iterable [duplicate]

This question already has answers here:
In Python, how do I determine if an object is iterable?
(23 answers)
Closed 3 years ago.
I have a class fib given below. It implements __iter__ and __next__. It is an iterable as well as its own iterator.
class fib(object):
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
value = self.curr
self.curr += self.prev
self.prev = value
return value
from collections import Iterable
print(isinstance(fib, Iterable))
The print statement returns False, I would expect it to return True
Checking if an object is iterable is correctly, as you've done, performed with:
isinstance(obj, collections.Iterable)
The problem here is you're supplying a class to isinstance and not an instance. It is False because isinstance will go ahead and check if type(fib) has an __iter__ method defined:
type(fib).__iter__ # AttributeError
This of course, isn't the case. type(fib) is type which doesn't define an __iter__ method.
If you supply an instance to it, it correctly prints True:
isinstance(fib(), Iterable) # True
because looking in type(fib()) it will find fib.__iter__.
Alternatively, feeding fib to issubclass performs a similar check that, instead, takes a class as a first argument:
issubclass(fib, Iterable) # True
Two extra minor things to point out:
Using object as an explicit base class is unnecessary in Python 3 (though, if you're developing code that runs on both Py2 and Py3, it is a good thing. (See Python class inherits object for more on this.)
According to PEP 8, class names should follow the CapWords convention, so fib should ideally be named Fib.

Returning individual string representation of all objects in list of objects [duplicate]

This question already has answers here:
How to concatenate (join) items in a list to a single string
(11 answers)
Closed 7 months ago.
Is it possible to return the string representation (using __str__) of all objects in a list of objects from a different classes own __str__ function?
Say I have a class that contains all methods being performed on Bar objects. Call this class Foo. Then:
class Foo:
def __init__(self):
self.bars = [] # contains Bar objects
def __str__(self):
return "This is the Foo object"
class Bar:
def __init__(self, arg1):
self.arg1 = arg1
def __str__(self):
return "This is Bar object: " + arg1
def main():
foo = Foo()
print(str(foo))
I want to print the string representation of all of the objects in self.bars WHEN I call main(), since creating an instance of the Bar object would not give me access to the string representation of all of the objects in self.bars.
To clarify, I am not asking the same question as in this post relating to the need of __repr__. Instead, I want to return each object's string representation individually, as with a for loop.
Is there anyway to do this, reasonably or unreasonably?
You already said how to do this, use a for loop.
return "".join([str(x) + "\n" for x in self.bars])

Categories

Resources