{"action":"created","issue":{"url":"https://api.github.com/repos/python/mypy/issues/1212","repository_url":"https://api.github.com/repos/python/mypy","labels_url":"https://api.github.com/repos/python/mypy/issues/1212/labels{/name}","comments_url":"https://api.github.com/repos/python/mypy/issues/1212/comments","events_url":"https://api.github.com/repos/python/mypy/issues/1212/events","html_url":"https://github.com/python/mypy/issues/1212","id":132599978,"number":1212,"title":"How to define a copy() function","user":{"login":"gvanrossum","id":2894642,"avatar_url":"https://avatars.githubusercontent.com/u/2894642?v=3","gravatar_id":"","url":"https://api.github.com/users/gvanrossum","html_url":"https://github.com/gvanrossum","followers_url":"https://api.github.com/users/gvanrossum/followers","following_url":"https://api.github.com/users/gvanrossum/following{/other_user}","gists_url":"https://api.github.com/users/gvanrossum/gists{/gist_id}","starred_url":"https://api.github.com/users/gvanrossum/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gvanrossum/subscriptions","organizations_url":"https://api.github.com/users/gvanrossum/orgs","repos_url":"https://api.github.com/users/gvanrossum/repos","events_url":"https://api.github.com/users/gvanrossum/events{/privacy}","received_events_url":"https://api.github.com/users/gvanrossum/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/python/mypy/labels/needs%20discussion","name":"needs discussion","color":"bfdadc"},{"url":"https://api.github.com/repos/python/mypy/labels/needs%20pep%20change","name":"needs pep change","color":"fbca04"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/python/mypy/milestones/6","html_url":"https://github.com/python/mypy/milestones/0.5","labels_url":"https://api.github.com/repos/python/mypy/milestones/6/labels","id":1616659,"number":6,"title":"0.5","description":"","creator":{"login":"ddfisher","id":245905,"avatar_url":"https://avatars.githubusercontent.com/u/245905?v=3","gravatar_id":"","url":"https://api.github.com/users/ddfisher","html_url":"https://github.com/ddfisher","followers_url":"https://api.github.com/users/ddfisher/followers","following_url":"https://api.github.com/users/ddfisher/following{/other_user}","gists_url":"https://api.github.com/users/ddfisher/gists{/gist_id}","starred_url":"https://api.github.com/users/ddfisher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ddfisher/subscriptions","organizations_url":"https://api.github.com/users/ddfisher/orgs","repos_url":"https://api.github.com/users/ddfisher/repos","events_url":"https://api.github.com/users/ddfisher/events{/privacy}","received_events_url":"https://api.github.com/users/ddfisher/received_events","type":"User","site_admin":false},"open_issues":41,"closed_issues":2,"state":"open","created_at":"2016-03-01T23:42:18Z","updated_at":"2016-05-05T16:23:15Z","due_on":null,"closed_at":null},"comments":10,"created_at":"2016-02-10T03:44:23Z","updated_at":"2016-05-16T15:32:15Z","closed_at":null,"body":"A colleague and I were wondering how to define a copy() method in a base class so that when called on an instance of a subclass it is known that it returns an instance of that subclass. We found the following solution:\r\n```\r\nT = TypeVar('T')\r\nclass Copyable:\r\n    def copy(self: T) -> T:\r\n        return self.__class__()  # type: ignore\r\nclass X(Copyable):\r\n    def foo(self):\r\n        pass\r\nx = X()\r\nx.foo()\r\nxx = x.copy()\r\nxx.foo()  # OK\r\n```\r\nNote that the `# type: ignore` is required for two reasons: you can't call `self.__class__()`, *and* it doesn't like the return type. Try e.g. `return self` and you get\r\n```\r\nx.py:5: error: Incompatible return value type: expected T`-1, got x.Copyable\r\n```\r\n\r\nIt works in `--py2` mode too:\r\n```\r\n    def copy(self):\r\n        # type: (T) -> T\r\n        return self.__class__()  # type: ignore\r\n````\r\n\r\n**UPDATE: This doesn't actually work. :-( See next comment.**"},"comment":{"url":"https://api.github.com/repos/python/mypy/issues/comments/219456921","html_url":"https://github.com/python/mypy/issues/1212#issuecomment-219456921","issue_url":"https://api.github.com/repos/python/mypy/issues/1212","id":219456921,"user":{"login":"JukkaL","id":1107911,"avatar_url":"https://avatars.githubusercontent.com/u/1107911?v=3","gravatar_id":"","url":"https://api.github.com/users/JukkaL","html_url":"https://github.com/JukkaL","followers_url":"https://api.github.com/users/JukkaL/followers","following_url":"https://api.github.com/users/JukkaL/following{/other_user}","gists_url":"https://api.github.com/users/JukkaL/gists{/gist_id}","starred_url":"https://api.github.com/users/JukkaL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JukkaL/subscriptions","organizations_url":"https://api.github.com/users/JukkaL/orgs","repos_url":"https://api.github.com/users/JukkaL/repos","events_url":"https://api.github.com/users/JukkaL/events{/privacy}","received_events_url":"https://api.github.com/users/JukkaL/received_events","type":"User","site_admin":false},"created_at":"2016-05-16T15:32:15Z","updated_at":"2016-05-16T15:32:15Z","body":"This seems to me like it could work. This would save us from having to define a special \"self type\", and we wouldn't need to update the implementation of `typing`. A pretty neat idea. \r\n\r\nA remaining open issue is enforcing a compatible `__init__` signature in subclasses. \r\n\r\nHere is a more detailed example of how this could work internally:\r\n\r\n```\r\nT = TypeVar('T', bound=Copyable)\r\nclass Copyable:\r\n    def copy(self: T) -> T:\r\n        t = self.__class__   # Type[T]\r\n        return t()   # T -> good\r\n```\r\n\r\nWe'd probably have to special case at least these things:\r\n\r\n* Implement `Type[T]` (which is a prerequisite for this).\r\n* Allow an annotation like this on `self` and enforce that the bound is compatible. Propagate the annotated type of `self` through the body of a method.\r\n* Substitute a value for the type variable when looking up a method of an instance or class that uses the trick. `C().copy` should have type like `() -> C` instead of `() -> T`.\r\n* Make `self.__class__` produce the right type (probably don't need to special case `self` here). Maybe also support `type(self)`?\r\n* Update overriding compatibility checks to know about this. Any override of `copy()` would also have to use a type variable for `self`.\r\n* Disallow using the type variable in an argument type as they are contravariant, or at least disallow most uses (maybe some could actually be supported).\r\n* It seems that using this `T` in a non-covariant position, such as in `List[T]`, of the return type should be rejected.\r\n\r\nIn phase 2, figure out `__init__` signature enforcement. We can leave this out in v1, but there is a risk that this will be tricky to retrofit. It would be nice to have a plausible design for phase 2 when we start working on the first phase."}}