限制类变量的修改,新实例除外

hus*_*sam 5 python python-3.x

这是以下帖子的后续问题(不需要检查链接即可理解问题)

类的计数器变量

我们将idCounterStudent 类设置为类变量,并计算创建的实例数。

这是班级:

class Student:
# A student ID counter
    idCounter = 0
def __init__(self):
    self.gpa = 0
    self.record = {}
    # Each time I create a new student, the idCounter increment
    Student.idCounter += 1
    self.name = 'Student {0}'.format(Student.idCounter)
Run Code Online (Sandbox Code Playgroud)

现在,我们实例化一些实例,然后检查 的值idCounter

student1 = Student()
student2 = Student()
student3 = Student()
student4 = Student()

Student.idCounter
4
Run Code Online (Sandbox Code Playgroud)

但是,如果您可以这样做,那么维护计数器就变得毫无意义:

Student.idCounter = 2000
Run Code Online (Sandbox Code Playgroud)

现在创建新实例:

student5 = Student()
Run Code Online (Sandbox Code Playgroud)

并检查idCounter

Student.idCounter

2001
Run Code Online (Sandbox Code Playgroud)

idCounter可以简单地搞砸计数器而无需运行__init__

如何创建一个仅在__init__运行时递增的计数器(或任何类变量)?并且不能通过从类中调用类变量来独立修改,如上所示。

是否有一种通用方法来限制使用语法修改类变量?

ClassName.ClassVariable = new_value
Run Code Online (Sandbox Code Playgroud)

谢谢。

Mik*_*ler 4

编辑

改进版本,property但原理相同:

class Meta(type):

    def __init__(cls, *args, **kwargs):
        cls.__value = 0
        super().__init__(*args, **kwargs)

    @property
    def idCounter(cls):
        return cls.__value

class Student(metaclass=Meta):

    def __init__(self):
        self.__class__._Meta__value += 1
Run Code Online (Sandbox Code Playgroud)

现在:

>>> s1 = Student()
>>> Student.idCounter
1
>>> s2 = Student()
>>> Student.idCounter
2
>>> Student.idCounter = 100    
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-64-a525899df18d> in <module>()
----> 1 Student.idCounter = 100

AttributeError: can't set attribute  
Run Code Online (Sandbox Code Playgroud)

旧版

使用描述符和元类:

class Counter:
    def __init__(self):
        self.value = 0
    def __get__(self, instance, cls):
        return getattr(instance, '_{}__hidden_counter'.format(instance.__name__ ))
    def __set__(self, instance, value):
        raise NotImplementedError

class Meta(type):
    idCounter = Counter()

class Student(metaclass=Meta):
    __hidden_counter = 0

    def __init__(self):
        Student.__hidden_counter += 1
Run Code Online (Sandbox Code Playgroud)

似乎实现了这一点:

>>> s1 = Student()
>>> Student.idCounter
1
>>> s2 = Student()
>>> Student.idCounter
2
>>> Student.idCounter = 200
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-51-dc2483b583f6> in <module>()
----> 1 Student.idCounter = 200

<ipython-input-46-b21e03bf3cb3> in __set__(self, instance, value)
      5         return getattr(instance, '_{}__hidden_counter'.format(instance.__name__ ))
      6     def __set__(self, instance, value):
----> 7         raise NotImplementedError
      8 
      9 class Meta(type):

NotImplementedError:
>>> Student.idCounter
2
Run Code Online (Sandbox Code Playgroud)

这仍然可以被故意打破:

>>> Student._Student__hidden_counter = 100
>>> Student.idCounter
100
Run Code Online (Sandbox Code Playgroud)

但并非偶然。