Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type hints: solve circular dependency [duplicate]

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) 
like image 722
Tamriel Avatar asked Nov 20 '15 23:11

Tamriel


People also ask

How do you resolve circular dependency issues?

The best solution is to avoid circular dependencies, of course, but it you're truly stuck, you can work around the issue by using property injection and RegisterInstance<T>(T t) (or its equivalent, if you're not using Autofac).

What is circular dependency error and how do you handle it?

A circular dependency is detected whenever two objects reference each other, in such a way that Power BI cannot process the objects. The details of why this happens are outlined in this article: Understanding Circular Dependencies in Tabular and PowerPivot – SQLBI.

What is typing callable python?

Short version, Callable is a type hint that indicates a function or other object which can be called. Consider a simple example below. The bar parameter is a callable object that takes two ints as parameters and returns an int.

What is circular dependency in python?

A circular dependency occurs when two or more modules depend on each other. This is due to the fact that each module is defined in terms of the other (See Figure 1). For example: functionA(): functionB() And functionB(): functionA() The code above depicts a fairly obvious circular dependency.


2 Answers

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.

like image 66
Martijn Pieters Avatar answered Oct 10 '22 06:10

Martijn Pieters


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() 
like image 38
Timothy C. Quinn Avatar answered Oct 10 '22 07:10

Timothy C. Quinn