(Использование переменных класса) Pythonic - или неприятная привычка, извлеченная из Java? - PullRequest
2 голосов
/ 30 сентября 2010

Здравствуйте, Pythoneers: следующий код - всего лишь макет того, что я пытаюсь сделать, но он должен проиллюстрировать мой вопрос.

Я хотел бы знать, является ли это грязной уловкой, которую я уловил в Java-программировании, или правильным и питонским способом выполнения вещей: в основном я создаю множество экземпляров, но мне нужно отслеживать «статические» данные все экземпляры по мере их создания.

class Myclass:
        counter=0
        last_value=None
        def __init__(self,name):
                self.name=name
                Myclass.counter+=1
                Myclass.last_value=name

И некоторые результаты использования этого простого класса, показывающие, что все работает, как я ожидал:

>>> x=Myclass("hello")
>>> print x.name
hello
>>> print Myclass.last_value
hello
>>> y=Myclass("goodbye")
>>> print y.name
goodbye
>>> print x.name
hello
>>> print Myclass.last_value
goodbye

Так это вообще приемлемый способ делать подобные вещи или анти-паттерн?

[Например, я не слишком рад, что могу установить счетчик как внутри класса (хорошо), так и за его пределами (плохо); также не заинтересован в необходимости использовать полное пространство имен «Myclass» внутри самого кода класса - просто выглядит громоздким; и, наконец, я изначально устанавливаю значения «Нет» - возможно, я делаю это со статическими типами языков?]

Я использую Python 2.6.2, и программа однопоточная.

Ответы [ 5 ]

7 голосов
/ 30 сентября 2010

Переменные класса, на мой взгляд, совершенно Pythonic.

Просто следи за одной вещью. Переменная экземпляра может скрывать переменную класса:

x.counter = 5  # creates an instance variable in the object x.
print x.counter  # instance variable, prints 5
print y.counter  # class variable, prints 2
print myclass.counter # class variable, prints 2
3 голосов
/ 30 сентября 2010

Не. Не. Есть. Stateful. Учебный класс. Переменные.

Это кошмар для отладки, поскольку объект класса теперь имеет специальные функции.

Классы с состоянием связывают две (2) несвязанные обязанности: состояние создания объекта и созданных объектов. Не объединяйте обязанности, потому что кажется, что они принадлежат друг другу. В этом примере подсчет созданных объектов является обязанностью Фабрики. Созданные объекты имеют совершенно не связанные обязанности (что не может быть легко выведено из вопроса).

Также, пожалуйста, используйте имена классов в верхнем регистре.

class MyClass( object ):
    def __init__(self, name):
            self.name=name

def myClassFactory( iterable ):
   for i, name in enumerate( iterable ):
       yield MyClass( name )

Счетчик последовательности теперь является частью фабрики, где должны поддерживаться состояние и счетчики. На отдельном заводе.

[Для тех, кто играет в Code Golf, это короче. Но дело не в этом. Дело в том, что у класса больше нет состояний.]

Непонятно из вопроса, как создаются экземпляры Myclass. Не имея никакой подсказки, о том, как использовать фабрику, можно сказать намного больше. Итерируемый - обычный преступник. Возможно, что-то, что перебирает список, файл или какую-то другую итеративную структуру данных.

Кроме того, для тех, кто только с лодки из Java, фабричный объект - это просто функция. Больше ничего не нужно.


Поскольку пример по данному вопросу совершенно неясен, трудно понять, почему (1) два уникальных объекта создаются с (2) счетчиком. Два уникальных объекта - это уже два уникальных объекта, и счетчик не нужен.

Например, на статические переменные в Myclass никогда не ссылаются нигде. Это делает его очень, очень трудным для понимания примера.

x, y = myClassFactory( [ "hello", "goodbye" ] ) 

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

2 голосов
/ 30 сентября 2010

Вы можете решить эту проблему, разделив код на два отдельных класса.

Первый класс будет для объекта, который вы пытаетесь создать:

class MyClass(object):
    def __init__(self, name):
        self.Name = name

А второй класс будет создавать объекты и отслеживать их:

class MyClassFactory(object):
    Counter = 0
    LastValue = None

    @classmethod
    def Build(cls, name):
        inst = MyClass(name)
        cls.Counter += 1
        cls.LastValue = inst.Name
        return inst   

Таким образом, вы можете создавать новые экземпляры класса по мере необходимости, но информация о созданных классах все еще будет правильной.

>>> x = MyClassFactory.Build("Hello")
>>> MyClassFactory.Counter
1
>>> MyClassFactory.LastValue
'Hello'
>>> y = MyClassFactory.Build("Goodbye")
>>> MyClassFactory.Counter
2
>>> MyClassFactory.LastValue
'Goodbye'
>>> x.Name
'Hello'
>>> y.Name
'Goodbye'

Наконец, этот подход позволяет избежать проблемы переменных экземпляра, скрывающих переменные классапотому что экземпляры MyClass не знают фабрики, которая их создала.

>>> x.Counter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'Counter'
2 голосов
/ 30 сентября 2010

Вам не нужно здесь использовать переменную класса;это вполне обоснованный случай для использования глобалов:

_counter = 0
_last_value = None
class Myclass(obj):
    def __init__(self, name):
        self.name = name

        global _counter, _last_value
        _counter += 1
        _last_value = name

У меня есть ощущение, что некоторые люди по привычке колотятся с глобалами, так что быстрый обзор может быть в порядке того, что не так, а ненеправильно - с глобальными переменными.

Глобальные переменные традиционно являются видимыми и изменяемыми переменными, без границ из любой точки программы.Это проблема с глобальными переменными в таких языках, как C. Это совершенно не относится к Python;эти "глобалы" относятся к модулю.Имя класса «Myclass» одинаково глобально;оба имени имеют одинаковую область видимости в модуле, в котором они содержатся. Большинство переменных - в Python, равно как и в C ++ - логически являются частью экземпляров объектов или имеют локальную область видимости, но это очищенное общее состояние для всех пользователей класса.

У меня нет сильной склонности к использованию переменных класса для этого (и использование фабрики совершенно не нужно), но я бы сделал это глобально.

1 голос
/ 30 сентября 2010

Это питон?Ну, это определенно более питонно, чем наличие глобальных переменных для счетчика и значения самого последнего экземпляра.

В Python сказано, что есть только один правильный способ сделать что-нибудь.Я не могу придумать лучшего способа реализовать это, так что продолжайте.Несмотря на то, что многие будут критиковать вас за «непитонное» решение проблем (например, ненужную объектную ориентацию, которую любят Java-кодеры, или отношение «сделай сам», которое дают многие из C и C ++), в большинстве случаевваши привычки Java не отправят вас в ад Python.

И кроме того, кого это волнует, является ли он "питоническим"?Это работает, и это не проблема производительности, не так ли?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...