Статические переменные Python

#python #static-variables

Вопрос:

Я пытаюсь использовать одну статическую переменную в качестве идентификатора для всех дочерних экземпляров этого родительского класса. Проблема, с которой я сталкиваюсь, заключается в двойном подсчете этого идентификатора, вероятно, потому, что родителю __init__ звонят дважды. Но если я удалю явный Parent().__init__() вызов, то счетчик вообще не увеличится. Как я могу заставить идентификатор увеличивать только единицы? Вот мой фрагмент кода:

 #!/usr/bin/python3
class Parent:
    cls_id = 0

    def __init__ (cls):
        cls.cls_inc_id()

    @classmethod
    def cls_inc_id (cls):
        cls.cls_id  = 1

    @classmethod
    def child_inc_id (self):
        self.id  = 1


class Child1 (Parent):

    def __init__ (self):
        Parent().__init__()
        self.id = super().cls_id
        print ("Child1 id: ", self.id)

class Child2 (Parent):

    def __init__ (self):
        Parent().__init__()
        self.id = super().cls_id
        print ("Child2 id: ", self.id)


child1 = Child1()
child2 = Child1()

child3 = Child2()
child4 = Child2()
 

Мой вывод таков:
%> ./static_vars.py

 Child1 id:  2
Child1 id:  4
Child2 id:  6
Child2 id:  8
 

Заранее спасибо

Комментарии:

1. Извините, чего именно вы пытаетесь достичь? Какой результат вы ожидаете ?

2. В качестве отступления, не используйте cls в качестве имени первого аргумента to __init__ . Условно, так и должно быть __init__ (self) , поскольку это метод экземпляра

Ответ №1:

Он увеличивается в два раза из-за Parent().__init() . Это создает совершенно новый объект, инициализирует его, а затем отбрасывает. Вы должны использовать super().__init__() , и в вашем __init__ method refer to родительском файле.cls_inc_id`. Тогда это сработает.

И, кстати, в Parent.__init__ вы называете параметр «cls», но на самом деле это «я» — это метод объекта, а не метод класса.

Новый код:

 #!/usr/bin/python3
class Parent:
    cls_id = 0

    def __init__ (self):
        print("Parent.__init__")
        Parent.cls_inc_id()

    @classmethod
    def cls_inc_id (cls):
        print("Parent.cls_inc_id")
        cls.cls_id  = 1

    @classmethod
    def child_inc_id (cls):
        print("child_inc_id")
        cls.id  = 1


class Child1 (Parent):

    def __init__ (self):
        print("Child1.__init__")
        super().__init__()
        self.id = super().cls_id
        print ("Child1 id: ", self.id)

class Child2 (Parent):

    def __init__ (self):
        print("Child2.__init__")
        super().__init__()
        self.id = super().cls_id
        print ("Child2 id: ", self.id)


child1 = Child1()
child2 = Child1()

child3 = Child2()
child4 = Child2()
 

Выход:

 Child1.__init__
Parent.__init__
Parent.cls_inc_id
1
Child1 id:  1
Child1.__init__
Parent.__init__
Parent.cls_inc_id
1
Child1 id:  2
Child2.__init__
Parent.__init__
Parent.cls_inc_id
1
Child2 id:  3
Child2.__init__
Parent.__init__
Parent.cls_inc_id
1
Child2 id:  4
 

Комментарии:

1. Также cls.cls_inc_id() должно быть Parent.cls_inc_id() . В противном случае он создает атрибут экземпляра вместо обновления атрибута класса.

2. Ха! Очень зоркий глаз!

3. @Barbar нет, это не так, потому cls_inc_id что classmethod

4. Да, это так, потому что он ВЫЗЫВАЕТ его как объектный метод. Попробовать это.

5. Я не совсем понимаю, что ты имеешь в виду. Он всегда будет получать объект класса в качестве первого параметра. Неважно, как вы его назовете, он не создаст атрибут экземпляра. Однако он создаст отдельные переменные класса в зависимости от того, как вы его называете. т. Е. Если вы используете self.cls_inc_id() , Parent.cls_inc_id метод передается Child1 либо Child2 в зависимости от типа self

Ответ №2:

Как уже упоминали другие, вы неправильно используете super конструктор, но я бы также ссылался на фактический класс в родительском классе, а не на экземпляр self , который вы неправильно назвали cls :

 class Parent:
    cls_id = 0

    def __init__(self):
        self.id = Parent.cls_id
        Parent.cls_id  = 1


class Child1(Parent):
    def __init__(self):
        super().__init__()
        print ("Child1 id: ", self.id)


class Child2(Parent):
    def __init__(self):
        super().__init__()
        print ("Child2 id: ", self.id)


child1 = Child1()
child2 = Child1()

child3 = Child2()
child4 = Child2()
 
 Child1 id:  0
Child1 id:  1
Child2 id:  2
Child2 id:  3
 

Если вам нужен отдельный идентификатор для каждого типа класса, вы можете использовать класс сопоставления словаря с идентификатором:

 from collections import defaultdict


class Parent:
    cls_ids = defaultdict(int)

    def __init__(self):
        self.id = Parent.cls_ids[type(self)]
        Parent.cls_ids[type(self)]  = 1
 
 Child1 id:  0
Child1 id:  1
Child2 id:  0
Child2 id:  1
 

Комментарии:

1. Спасибо за совет по использованию пакета коллекций. Я попробую это сделать.