2013-02-12 4 views
1

Я пытаюсь добавить переменную в класс, который содержит экземпляры класса. Ниже приведена сокращенная версия моего кода.Python Instantiate Class Within Class Определение

class Classy : 
    def __init__(self) : 
     self.hi = "HI!" 
    # "CLASSIES" variable holds instances of class "Classy" 
    CLASSIES = [] 
    for i in xrange(0,4) : 
     CLASSIES.append(Classy()) 

При запуске кода я получаю следующее сообщение об ошибке.

Traceback (most recent call last): 
    File "classy.py", line 6, in Classy 
    CLASSIES.append(Classy()) 
NameError: name 'Classy' is not defined 

Есть еще один способ добавить экземпляры класса к классу/статической переменной внутри этого класса?

+0

Настоящий вопрос - это то, что вы пытаетесь выполнить с помощью этого перемещения. – joojaa

+0

@joojaa Я бы не сказал, что варианты использования для этого - это * то, что неясно. –

+0

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

ответ

2

Тело класса выполнено до создания класса. Поэтому вы пытаетесь создать экземпляр класса до его существования. Вы можете присоединить экземпляры к классу, но вы должны создать их после того, как тело класса закончил, например:

class Classy(object): 
    def __init__(self): 
     self.hi = "HI!" 
    CLASSIES = [] 

for i in xrange(4): 
    Classy.CLASSIES.append(Classy()) 

Однако, я предлагаю вам первый думать долго и трудно, действительно ли вам это нужно эффективно глобального список, и нужно ли вам, чтобы он был частью объекта класса. Лично я почти никогда не делаю ничего подобного.

+0

Это обман моего ответа. Собственно, вы также совершили ту же ошибку, что и я, - она ​​также должна быть 'Classy.CLASSIES.append()'. –

+0

@Lattyware Действительно ли это обман, если он содержит значительно больше объяснений рядом с кодом? Кроме того, код тоже немного отличается. Но спасибо за указание на ошибку! – delnan

+0

Ваше объяснение - это дубликат того, что BrenBarn говорит в своем ответе, и ваше предложение в конце кажется ошибочным для меня - это не * эффективно глобальное * - это классный уровень, и, хотя это редкость, есть примеры использования чего-то подобного, пока не набраны. Разница в коде также тривиальна. Это не плохой ответ, просто я не вижу в нем ничего, что не было сказано. –

2

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

class Classy : 
    CLASSIES = [] 

    def __init__(self) : 
     self.hi = "HI!" 

Classy.CLASSIES = [Classy() for _ in xrange(0,4)] 

(Здесь с помощью list comprehension для удобствами, так как это наиболее читаемый и эффективный способ создания списка).

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

+0

Да, ** list comp. ** строит список и что ** создает новый список **, отличный от того, который был результатом выполнения определения класса, в то время как может быть важно, чтобы тот же список будет только ** обновлено **. См. Мое изменение в моем ответе. – eyquem

+0

@eyquem Это первоначальное создание, поэтому это не имеет значения. Эмутируйте, что просто удалите начальное создание в классе - я просто чувствовал, что стоит положить туда, поскольку маловероятно, что это будет иметь значение, и дает подсказку о том, что список будет существовать (в том числе и для редакторов). Если вам почему-то нужно * оно существовать, а личность останется неизменной, просто сделайте '+ =' вместо '='. –

+0

Ну, если это не имеет значения, потому что это начальное создание, тогда строка '' CLASSIES = [] '' не требуется в определении класса. Так обстоит дело в коде BrenBarn – eyquem

1

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

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

def addClassy(cls): 
    cls.CLASSIES = [cls() for a in xrange(4)] 
    return cls 

@addClassy 
class Classy(object): 
    pass 

>>> Classy.CLASSIES 
0: [<__main__.Classy object at 0x000000000289A240>, 
<__main__.Classy object at 0x000000000289A518>, 
<__main__.Classy object at 0x000000000289A198>, 
<__main__.Classy object at 0x000000000289A208>] 
+0

Пока это работает, он чувствует себя немного ... выключено - мне гораздо труднее увидеть, что класс имеет атрибут CLASSIES и что будет в нем. –

+1

@Lattyware: Это зависит от того, как часто вам нужно это делать. Если вам нужен этот шаблон для нескольких классов, имеет смысл создать один декоратор, чтобы сделать это, а не повторять код, чтобы сделать это вручную для каждого класса. – BrenBarn

+0

В этом случае это будет иметь больше смысла, да. –

2

Мне кажется, что вы хотите получить, что:

class Classy : 
    CLASSIES = [] 
    def __init__(self) : 
     self.hi = "HI!" 
     Classy.CLASSIES.append(self) 

for i in xrange(4): 
    Classy() 

for x in Classy.CLASSIES: 
    print x 

результата

<__main__.Classy instance at 0x011DF3F0> 
<__main__.Classy instance at 0x011DF440> 
<__main__.Classy instance at 0x011DF418> 
<__main__.Classy instance at 0x011DF2B0> 

EDIT

Обратите внимание, что с кодом Lattyware:

class Classy : 
    CLASSIES = [] 
    idC = id(CLASSIES) 
    def __init__(self) : 
     self.hi = "HI!" 
     #Classy.CLASSIES.append(self) 


Classy.CLASSIES = [Classy() for _ in xrange(0,4)] 

print Classy.idC 
print id(Classy.CLASSIES) 
print 'Classy.idC==id(Classy.CLASSIES) :',Classy.idC==id(Classy.CLASSIES) 

результат

18713576 
10755928 
Classy.idC==id(Classy.CLASSIES) : False 

В то время как для цикла delnan'code он не отображается.

Однако это легко исправить:
написание
Classy.CLASSIES[:] = [Classy() for _ in xrange(0,4)]
или
Classy.CLASSIES.extend(Classy() for _ in xrange(0,4))
вместо
Classy.CLASSIES = [Classy() for _ in xrange(0,4)]
это зависит от того, что требуется.

EDIT 2

Методы могут ссылаться на глобальные имена таким же образом, как обычные функции. Глобальная область, связанная с методом, представляет собой модуль , содержащий его определение. (Класс никогда не используется в качестве глобального масштаба.)

http://docs.python.org/2/tutorial/classes.html#class-definition-syntax

Класс имеет пространство имен, реализованный в словаре объекта. ссылки класса атрибутов переводятся в поиски в этом словаре, например, C.x переводится на C.__dict__["x"]

http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes

class Classy : 
    CLASSIES = [] 

print '"CLASSIES" in globals()',"CLASSIES" in globals() 
print '"CLASSIES" in Classy.__dict__ ==',"CLASSIES" in Classy.__dict__ 

результат

"CLASSIES" in globals() False 
"CLASSIES" in Classy.__dict__ == True 

Delnan, как вы продолжать делать вид что CLASSIES является глобальным?
Я что-то не понял в ваших спорах с Lattyware?

+0

Это предполагает, что OP хочет, чтобы * каждый * класс добавлялся в список. –

+0

@Lattyware Да, это предполагает, но я написал «мне кажется», что означает, что я не был уверен. И я до сих пор не уверен. Досадно, что часто бывает трудно понять вопросы, t дать достаточно информации о том, чего они действительно хотят, и о контексте проблемы. – eyquem

+0

Это не ошибка. Почему бы вам сделать другую ссылку на этот список, а не получить доступ к нему через «Classy.CLASSIES» - это возможно, но вам нужно будет сделать это, прежде чем заканчивать определение класса, а это значит, что вам никогда не понадобится это делать. –