Самый «питонный» способ организации атрибутов класса, аргументов конструктора и значений по умолчанию конструктора подкласса? - PullRequest
16 голосов
/ 13 июля 2009

Будучи относительно новым для Python 2, я не уверен, как лучше организовать свои файлы классов самым «питоническим» способом. Я не стал бы спрашивать об этом, кроме того факта, что в Python, похоже, есть довольно много способов сделать то, что сильно отличается от того, что я привык ожидать от языков, к которым я привык.

Изначально я просто относился к классам так, как я обычно относился к ним на C # или PHP, что, конечно, заставило меня запутаться повсюду, когда я в конце концов обнаружил изменяемые значения: - 1003 *

class Pants(object):
    pockets = 2
    pocketcontents = []

class CargoPants(Pants):
    pockets = 200

p1 = Pants()
p1.pocketcontents.append("Magical ten dollar bill")
p2 = CargoPants()

print p2.pocketcontents

Хлоп! Не ожидал этого!

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

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

class ClassWithLotsOfAttributes(object):
    def __init__(self, jeebus, coolness='lots', python='isgoodfun', 
             pythonic='nebulous', duck='goose', pants=None, 
             magictenbucks=4, datawad=None, dataload=None,
             datacatastrophe=None):

        if pants is None: pants = []
        if datawad is None: datawad = []
        if dataload is None: dataload = []
        if datacatastrophe is None: datacatastrophe = []
        self.coolness = coolness
        self.python = python
        self.pythonic = pythonic
        self.duck = duck
        self.pants = pants
        self.magictenbucks = magictenbucks
        self.datawad = datawad
        self.dataload = dataload
        self.datacatastrophe = datacatastrophe
        self.bigness = None
        self.awesomeitude = None
        self.genius = None
        self.fatness = None
        self.topwise = None
        self.brillant = False
        self.strangenessfactor = 3
        self.noisiness = 12
        self.whatever = None
        self.yougettheidea = True

class Dog(ClassWithLotsOfAttributes):
    def __init__(self, coolness='lots', python='isgoodfun', pythonic='nebulous', duck='goose', pants=None, magictenbucks=4, datawad=None, dataload=None, datacatastrophe=None):
        super(ClassWithLotsOfAttributes, self).__init__(coolness, python, pythonic, duck, pants, magictenbucks, datawad, dataload, datacatastrophe)
        self.noisiness = 1000000

    def quack(self):
        print "woof"

Небольшая глупость в стороне (я не могу помочь себе, когда готовлю эти искусственные классы примеров), предполагая, что у меня есть реальная потребность в наборе классов с таким количеством атрибутов, я предполагаю, что мои вопросы:

  • Какой самый «эмпирический» способ объявления класса с таким количеством атрибутов? Лучше ли помещать их в класс, если значение по умолчанию является неизменяемым, например, Pants.pockets, или лучше поместить их в конструктор, например, ClassWithLotsOfAttributes.noisiness?

  • Есть ли способ устранить необходимость переопределения значений по умолчанию для всех аргументов конструктора подкласса, как в Dog .__ init__? Должен ли я вообще включать столько аргументов в значения по умолчанию?

Ответы [ 2 ]

6 голосов
/ 13 июля 2009
  • Если атрибуты будут отличаться от экземпляра к примеру сделать их экземпляром атрибут, т.е. создать их внутри __init__ используя self иначе, если им нужно быть разделенным между экземплярами класса как константа, ставь их на уроке уровень.

  • Если ваш класс действительно нужно пройти, так много аргументов в __init__, пусть получить список аргументов использования класса и аргументы ключевых слов, например

class Dog(ClassWithLotsOfAttributes):
    def __init__(self, *args , **kwargs):
        super(ClassWithLotsOfAttributes,    self).__init__(*args , **kwargs)
        self.coolness = "really cool!!!
  • Нет необходимости передавать все переменные, кроме нескольких важных, в __init__, класс может предполагать некоторые по умолчанию и пользователь может изменить их позже, если потребуется.
  • Используйте 4 пробела вместо табуляции.

  • если вам нужно добавить дополнительный аргумент arg, к Dog и ключевому слову arg old тоже

class CoolDog(ClassWithLotsOfAttributes):
    def __init__(self, bite, *args , **kwargs):
        self.old = kwargs.pop('old', False) # this way we can access base class args too
        super(ClassWithLotsOfAttributes,    self).__init__(*args , **kwargs)
        self.bite = bite
        self.coolness = "really really cool!!!

различные способы использования CoolDog

CoolDog(True)
CoolDog(True, old=False)
CoolDog(bite=True, old=True)
CoolDog(old=True, bite=False)
0 голосов
/ 13 июля 2009

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

Если вам действительно нужно иметь столько атрибутов, я думаю, вам придется смириться с их назначением, тем более что вам нужны значения по умолчанию для них всех. Вам не нужно переназначать настройки по умолчанию в ваших подклассах (я вижу, что Anurag Uniyal показал как.)

Вы должны назначить их self, а не как атрибуты класса. Обратите внимание на разницу:

class Class1(object):
    attr1 = 'abc'

class Class2(object):
    def __init__(self):
        self.attr1 = 'abc'

Class1.attr1 # returns 'abc'
c = Class1()
c.attr1 # Also returns 'abc'
Class1.attr1 = 'def'
c.attr1 # Returns 'def'!
c.attr1 = 'abc' # Now the c instance gets its own value and will show it
                # independently of what Class1.attr1 is. This is the same
                # behavior that Class2 exhibits from the start.
...