Построение объекта Python 3: какой самый Pythonic / принятый способ? - PullRequest
8 голосов
/ 01 марта 2010

Имея фон в Java, который является очень многословным и строгим, я нахожу возможность мутировать объекты Python, чтобы давать им поля, отличные от представленных конструктору, действительно "некрасиво".

Пытаясь приучить себя к питонскому образу мышления, Мне интересно, как я должен позволять строить свои объекты .

Мой инстинкт должен передавать поля во время строительства , например:

def __init__(self, foo, bar, baz=None):
    self.foo = foo
    self.bar = bar
    self.baz = baz

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

def __init__(self, field_map):
    self.foo = field_map["foo"]
    self.bar = field_map["bar"]
    self.baz = field_map["baz"] if baz in field_map else None

Другой механизм, который я могу придумать, состоит в том, чтобы иметь поля, добавленные в другом месте , например:

class Blah(object):

    def __init__(self):
        pass

...

blah = Blah()
blah.foo = var1

Но так как это way слишком свободно для меня.

(Полагаю, проблема в моей голове заключается в том, как я справляюсь с интерфейсами в Python ...)

Итак, еще раз повторю вопрос: как мне конструировать свои объекты в Python? Есть ли принятая конвенция?

Ответы [ 2 ]

9 голосов
/ 01 марта 2010

Первое, что вы описываете, очень распространено. Некоторые используют короче

class Foo:
   def __init__(self, foo, bar):
       self.foo, self.bar = foo, bar

Ваш второй подход не распространен, но похожая версия такова:

class Thing:
   def __init__(self, **kwargs):
       self.something = kwargs['something']
       #..

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

t = Thing(something=1)

Это может быть дополнительно изменено до

class Thing:
   def __init__(self, **kwargs):
       self.__dict__.update(kwargs)

позволяет

t = Thing(a=1, b=2, c=3)
print t.a, t.b, t.c # prints 1, 2, 3

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

class Thing:
    keywords = 'foo', 'bar', 'snafu', 'fnord'
    def __init__(self, **kwargs):
        for kw in self.keywords:
            setattr(self, kw, kwargs[kw])

Есть много вариаций, нет единого стандарта, о котором я знаю.

3 голосов
/ 01 марта 2010

Я не видел многих из ваших field_map в реальной жизни. Я думаю, что это имело бы смысл только в том случае, если бы вы использовали field_map в другом месте вашего кода.

Относительно вашего третьего примера: даже если вам не нужно присваивать им (кроме None), обычной практикой является явное объявление атрибутов в методе __init__, так что вы легко увидите, какие свойства ваш объект имеет.

Таким образом, следующее лучше, чем просто использовать пустой метод __init__ (вы также получите более высокий pylint счет за это):

class Blah(object):
    def __init__(self):
        self.foo = None
        self.bar = None

blah = Blah()
blah.foo = var1

Проблема с этим подходом заключается в том, что ваш объект может быть в нечетко определенном состоянии после инициализации, потому что вы еще не определили все свойства вашего объекта. Это зависит от логики вашего объекта (логика в коде и значении) и от того, как работает ваш объект. Однако если это так, я бы посоветовал вам , а не сделать это таким образом. Если ваш объект полагается на foo и bar для значимого определения, вы должны действительно поместить их в ваш __init__ метод.

Если, однако, свойства foo и bar не являются обязательными, вы можете определить их позже.

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

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