Skip to content

Disallow access to instance variable via class #240

Closed

Description

@JukkaL

Currently an instance variable can be accessed using the class, which is wrong. For example, this code is accepted by the type checker:

class A:
    def f(self) -> None:
        self.x = 1
A.x = 1 # accepted, but should be an error

Class variables need to be assigned in the class body:

class A:
    x = 1
A.x = 1 # ok, and should be ok

Activity

  1. JukkaL commented on May 17, 2015

    @JukkaL
    CollaboratorAuthor

    Actually, I'm no longer convinced that this is a bug. Turning this into an enhancement proposal.

  2. added and removed
    bugmypy got something wrong
    on May 17, 2015
  3. gvanrossum commented on May 17, 2015

    @gvanrossum
    Member

    Yeah, this may just be a somewhat unconventional wy of setting the default.

  4. JukkaL commented on Oct 13, 2015

    @JukkaL
    CollaboratorAuthor

    Postponing this as I'm not sure if this is a good idea but might still be worth considering.

  5. mark-kubacki commented on Feb 18, 2022

    @mark-kubacki

    The OP has it as writing—I ran into this erroneously reading a variable; consider:

    from __future__ import annotations
    import logging
    
    from typing import Type
    
    
    class A:
        def __init__(self):
            self._log = logging.getLogger()
            pass
    
        @classmethod
        def from_spam(cls: Type[A], spam):
            if spam:
                cls._log.warning("got spam")  # accepted, but should be an error
            return cls()
  6. gsakkis commented on Sep 11, 2023

    @gsakkis

    I ran into a situation which is almost the opposite problem of the original one: not only a given attribute is deliberately accessible by both the instance and the class, but the type in each case different. Unsurprisingly mypy complains about Incompatible types in assignment.

    Minimal example:

    class A:
        x = "a"
    
        def __init__(self) -> None:
            self.x = 1
    
    assert A().x == 1
    assert A.x == "a"

    I tried annotating x both as an instance and class variable but this caused a Name "x" already defined error without getting rid of the first one:

    class A:
        x: int
        x: ClassVar[str] = "a"
    
        def __init__(self) -> None:
            self.x = 1
  7. vtgn commented on Oct 15, 2024

    @vtgn

    Hello!
    2024 here! What are the news since 2013?!

    With Python 3.12.6 and mypy 1.11.2, mypy says still nothing on this case, but Pylint and Pylance do it, and there is an error during the code execution.

    Plus, for this close similar case:

    class A:
        id: Final[int] = 5
    
        def __init__(self):
            self.id = 10
    
    a = A()
    
    print(A.id) # => 5
    print(a.id) # => 10
    print(a.__class__.id) # => 5

    mypy says Cannot assign to final attribute "id" mypy(misc) on the self.id = 10 line, which is incorrect because self.id defined in the constructor is considered as an instance attribute by Python, and not the class attribute A.id. :/
    Pylance does exactly the same mistake. >_< !!!

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

      Issue actions