Skip to content

Support for immutable instance variables (feature) #2795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Stiivi opened this issue Feb 2, 2017 · 4 comments
Closed

Support for immutable instance variables (feature) #2795

Stiivi opened this issue Feb 2, 2017 · 4 comments

Comments

@Stiivi
Copy link

Stiivi commented Feb 2, 2017

Provide information for the type checker about instance variables that are immutable once initialized.

Motivation

There are situations where it might be desirable that some or all of the instance variables remain unchanged during lifetime of the object. It would be great if the type checker issued an error or warning when a variable with a special annotation is assigned to outside of permitted scope.

Proposed Solution

Introduce Immutable[T]:

class Attribute:
    name: Immutable[str]
    def __init__(self, name: str) → None
        self.name = name

Assignment to Immutable variables should be allowed only in the __init__ method of a class. The following should cause an error, no matter of scope:

attr = Attribute("foo")
# Causes type checking error:
attr.name = "bar"

Error should occur even in other methods of the class that defines the Immutable variable:

class Attribute:
    name: Immutable[str]
    # ... (definition as above) ...

    def set_name(self, new_name: str) → None:
        # Should cause type checking error:
        self.name = new_name

This is an additive change, does not break existing code.

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 7, 2017

This looks like a duplicate of #1214, assuming that you are only thinking about rejecting assignments to the attribute. An alternative approach would be to reject also mutation of the assigned object (e.g. can't append to an immutable list) but this would be much more complicated.

@gvanrossum
Copy link
Member

Looks pretty clear that he meant exactly #1214. However, in normal usage, the term "immutable" is a slightly different concept, referring to the conceptual value of an object not being allowed to change over its lifetime. That concept has implications for the behavior of __eq__ and is required for the presence of __hash__. What's intended here and in #1214 is more like a read-only attribute.

@Stiivi
Copy link
Author

Stiivi commented Feb 7, 2017

For now I was thinking mostly about assignment, which as you say is covered in #1214. Rejecting mutation would be great to have – that would require annotation of methods as mutating similar to Swift:

class Point:
    x: float
    y: float

    def __init__(x: float, y: float):
        self.x = x
        self.y = y

    @mutating
    def move_by(dx: float, dy: float) → None:
        self.x += dx
        self.y += dy

p: Point = Point(0, 0)
q: Immutable[Point] = Point(0, 0)

# This is ok:
p.move_by(10, 10)

# This should cause an error:
q.move_by(10, 10)

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 7, 2017

Controlling mutation is probably a big enough change that it's likely not going to happen, at least in the near future. It may require major typeshed changes, so it should probably discussed at the typing issue tracker (https://github.com/python/typing/issues).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants