HansSnah
HansSnah

Reputation: 2250

Define what class contains

Ok, this might be a very stupid question and it might not be possible, or I am thinking in a twisted way. So, consider this:

class MyClass:
    def __init__(self, x):
        self.x = x
        self.y = self.x ** 2


class MyList:
    def __init__(self, list):
        self.list = list

    def __iter__(self):
        return iter(self.list)

foo = [MyClass(x=1), MyClass(x=2)]
bar = MyList(list=foo)

for i in bar:
    print(i.x, i.y)

Ok, here we have two classes: MyClass is just something very ordinary and nothing special. It has two instance attributes: x and y. MyList, on the other hand, should be a class which defines an iterator which should only contain elements from the first class MyClass!

Then I create two instances of MyClass, put them in a list foo, and use this list to create an instance of MyList (bar).

Now, again, the list passed to MyList should only contain MyClass elements! What I want is Python to be aware of this fact! When I write a loop where I iterate over the contents of bar, I would like that Python knows that the elements in bar are MyClass objects.

A major inconvenience in my program for now is that Python is not aware of this and the autocompletion does not work. In other words: In the loop, when I write i., I would like all the possible arguments to pop up (in this case x and y). Can I define some magic method in Python for MyList which makes Python aware of the contents of this list?

Upvotes: 5

Views: 219

Answers (3)

das-g
das-g

Reputation: 9994

PEP 484

In Python ≥ 3.0, you can use function annotations (PEP 3107) with the type hinting semantics of PEP 0484. Although the latter proposal was only accepted in Python 3.5 and will be provisional until the release of 3.6, it's syntactically backwards compatible to all Python versions supporting PEP 3107, so using type hint annotations in any 3.x version of Python should at least not hurt.[1]

Whether it helps your IDE or interactive interpreter (REPL) to do better autocompletion is up to that IDE or interpreter, and maybe its settings, even for Python ≥ 3.5.

For Python 2, an alternative notation using comments is available, that tools supporting PEP 0484 might or might not respect.

Add the type hints you care about

Let's look how the annotation-based hinting (Python 3.x) would look for your code. (Comment based, Python-2.x-compatible hinting is left as an exercise for the reader.)

To indicate that iterating over MyList instances yields MyClass objects, we hint the return type of __iter__() by appending a -> (Arrow made of a minus and a greater-than sign) after the function definition's colon, followed by the type of the return value. __iter__() itself doesn't return a MyClass, it returns an iterator. To indicate that it shall be an iterator over MyClass, we use the generic Iterator abstract base class from the typing module[2]:

from typing import Iterator

class MyClass:
    # ...

class MyList:
    # ...

    def __iter__(self): -> Iterator[MyClass]
        return iter(self.list)

To be able to hold this promise, self.list must contain only MyClass instances. So let's kindly ask our callers to provide such, by type-hinting the __init__() argument:

from typing import Iterator, Iterable

# ...

class MyList:
    def __init__(self, list: Iterable[MyClass]):
        self.list = list

    def __iter__(self): -> Iterator[MyClass]
        return iter(self.list)

Note that I chose the Iterable generic abstract base class, not the more specific List generic abstract base class (nor Mapping, Sequence, or AbstractSet), as your implementation only relies on iter(...).


[1] except maybe readability when overused. So as Mark writes, "please use type-hints responsibly" if you do use them.

[2] included in Python ≥ 3.5. For lower versions, use the backport installable with pip install typing.

Upvotes: 2

Jacques Supcik
Jacques Supcik

Reputation: 659

If you are using the PyCharm IDE, then you can do this:

for i in bar:
    assert isinstance(i, MyClass)

or this:

for i in bar: # type: MyClass

Upvotes: 3

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160377

Python is not aware of any types until you actually run your code. That is when types are checked and actions based on the type occur.

What you are looking for is an IDE/REPL feature while developing code. In the default Python REPL this sort of look-up might not be available. In more sophisticated enviroments this does actually happen and you can see the attributes for a given class.

For example, in the IPython REPL I often use, when inside the loop:

for i in bar:

I press i. and hit tab both elements i.x i.y are presented. This is because the REPL has been developed to allow for greater introspection while developing code.

I'm pretty sure most well known IDEs provide this; Pythons default REPL is rather simplistic (3.5's has some autocomplete, though! :D) and doesn't offer way too much.

Upvotes: 1

Related Questions