Skip to content

SelfType or another way to spell "type of self" (or, How to define a copy() function) #1212

Closed
@gvanrossum

Description

@gvanrossum
Member

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:

T = TypeVar('T')
class Copyable:
    def copy(self: T) -> T:
        return self.__class__()  # type: ignore
class X(Copyable):
    def foo(self):
        pass
x = X()
x.foo()
xx = x.copy()
xx.foo()  # OK

Note 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

x.py:5: error: Incompatible return value type: expected T`-1, got x.Copyable

It works in --py2 mode too:

    def copy(self):
        # type: (T) -> T
        return self.__class__()  # type: ignore

UPDATE: This doesn't actually work. :-( See next comment.

Activity

JukkaL

JukkaL commented on Feb 10, 2016

@JukkaL
Collaborator

Hmm if this works it's not by design. I actually get errors when type checking the example:

t.py:11: error: Need type annotation for variable   (on line xx = x.copy())
t.py:12: error: None has no attribute "foo"

To make this work properly we could provide a new kind of type: a "self" type, which is basically the concrete type of self (in a derived class it would refer to the derived class, even if the definition is inherited). It's been used in a bunch of languages, though I'm not sure if any mainstream language has it. To make it work well we'd also need to support calling self.__class__.

We could have something like this:

from typing import SelfType

class Copyable:
    def copy(self) -> SelfType:
        return self.__class__()  # TODO still need to be able to call self.__class__
class X(Copyable):
    def foo(self):
        pass
x = X()
x.foo()
xx = x.copy()
xx.foo()  # OK

SelfType would probably only be usable in a covariant position such as in a return type. self and self.__class__(...) would have the type SelfType.

gvanrossum

gvanrossum commented on Feb 10, 2016

@gvanrossum
MemberAuthor

Oh, I could have sworn that it worked but it was late. :-( So yes, I guess this is two feature requests: SelfType and calling self.__class__.

changed the title [-]How to define a copy() function -- clever solution![/-] [+]How to define a copy() function[/+] on Feb 10, 2016
gvanrossum

gvanrossum commented on Feb 10, 2016

@gvanrossum
MemberAuthor

(It seems to work when the class is generic, but who knows what other effects that has.)

added this to the Future milestone on Mar 1, 2016
modified the milestone: Future on Mar 1, 2016
added a commit that references this issue on Apr 15, 2016
f3cd07d

38 remaining items

added a commit that references this issue on Apr 9, 2017

python/mypy#1212 has been fixed, so this comment can be replaced

kyprifog

kyprifog commented on Apr 28, 2020

@kyprifog

For anyone else that stumbles on this and is looking for the SelfType application, this helped me:

#3661

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @rwbarton@ddfisher@drakedevel@kyprifog@JukkaL

        Issue actions

          SelfType or another way to spell "type of self" (or, How to define a copy() function) · Issue #1212 · python/mypy