Type hints: solve circular dependency

The following produces NameError: name 'Client' is not defined. How can I solve it?

class Server():
    def register_client(self, client: Client)
        pass


class Client():
    def __init__(self, server: Server):
        server.register_client(self)

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

You can use a forward reference by using a string name for the not-yet-defined Client class:

class Server():
    def register_client(self, client: 'Client')
        pass

As of Python 3.7, you can also postpone all runtime parsing of annotations by adding the following __future__ import at the top of your module:

from __future__ import annotations

at which point the annotations are stored as string representations of the abstract syntax tree for the expression; you can use typing.get_type_hints() to resolve those (and resolve forward references as used above).

See PEP 563 — Postponed Evaluation of Annotations for details; this behaviour will be the default in Python 4.0.

Method 2

If you are on Python 3.7+, use from __future__ import annotations as mentioned in another answer. However, if you cannot use 3.7 yet due to OS limitation (like Cygwin as of 2019-06-03), you can use Forward References module to satisfy these types of forward/circular dependency issues.

Pardon the contrived example but this should illustrate the usefulness of this methodology.

class Server():
    clients: list = None

    def __init__(self):
        self.clients=[]

    def register_client(self, client: 'Client') -> None:
        self.clients.append(client)
        print('Client `%s` registered with server' % client.name)

    def print_clients(self) -> None:
        for i, client in enumerate(self.clients):
            print('client %i: %s' % (i, client.name))

    @staticmethod
    def build_clone(server: 'Server') -> 'Server':
        svr_new: Server = Server()
        for client in server.clients:
            svr_new.register_client(client)
        return svr_new

class Client():
    name: str = None
    def __init__(self, name: str, server: 'Server'):
        self.name = name
        server.register_client(self)


svr = Server()
cli = Client('foo', svr)
svr.print_clients()

svr_clone = Server.build_clone(svr)
svr_clone.print_clients()


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x