Проблема создания нового отдельного экземпляра класса в Python 2.7 - PullRequest
0 голосов
/ 20 декабря 2018

У меня есть класс, который, по сути, должен иметь возможность инициализировать себя и устанавливать внутренние значения на основе строки текста (строки).Кажется, это работает нормально при создании одного экземпляра, однако при создании второго экземпляра строка текста, передаваемая в первый экземпляр, по-видимому, подается в одну из внутренних переменных во втором экземпляре!Конструктор init для класса определяется значениями по умолчанию для всех соответствующих параметров, которые передаются соответствующим внутренним переменным.В частности, параметр 'prefixComments' имеет значение по умолчанию [], что означает, что для self.PrefixComments должно быть установлено то же самое (пустой список) ... к сожалению, он, очевидно, становится строкой текстаэто было использовано для создания предыдущего объекта (или, по крайней мере, это мое лучшее предположение).

Я действительно озадачен тем, что здесь происходит.Любые идеи о том, как это исправить.код и вывод следующие:

код:

import collections
from collections import OrderedDict
import numpy as np
import string
import re
import gc

class AtomEntry:
    def __init__(self,atomName="",atomType="",charge=0.0,
                 comment="",prefixComments=[],
                 verbose=False):
        self.Name=atomName
        self.Type=atomType
        self.Charge=charge
        self.Comment=comment
        self.PrefixComments=prefixComments

    def from_line_string(self,line):
        #returns 1 if an error is encountered, 0 if successful
        lineTokens=line.split()
        if len(lineTokens)<4:
            print("Error: too few entries to construct ATOM record")
            return(1)
        elif lineTokens[0] != "ATOM":
            print("ERROR: atom entries must begin with the keyword 'ATOM'")
            return(1)
        else:
            self.Name=lineTokens[1]
            self.Type=lineTokens[2]
            self.Charge=float(lineTokens[3])
            if len(lineTokens) >=5:
                self.Comment=string.replace(
                    s=' '.join(lineTokens[5:len(lineTokens)]),
                    old='!',new='')
        return(0)

    def to_str(self,nameOnly=False):
        if nameOnly:
            return "%s"%(self.Name)
        else: 
            return repr(self)

    def __repr__(self):
        outStrs=self.PrefixComments
        outStrs.append(
            "ATOM %6s %6s %6.3f !%s"%(
                self.Name,self.Type,self.Charge,self.Comment))
        return ''.join(outStrs)

tempAtom1=AtomEntry()
tempAtom1.from_line_string("ATOM S1     SG2R50 -0.067 !   93.531")
print tempAtom1
print ""
gc.collect()
tempAtom2=AtomEntry()
tempAtom2.from_line_string("ATOM C1     CG2R53  0.443 !   83.436")
print tempAtom2
print""

print tempAtom2.Name
print tempAtom2.Type
print tempAtom2.Charge
print tempAtom2.Comment
print tempAtom2.PrefixComments

gc.collect()

вывод:

ATOM     S1 SG2R50 -0.067 !93.531

ATOM     S1 SG2R50 -0.067 !93.531ATOM     C1 CG2R53  0.443 !83.436

C1
CG2R53
0.443
83.436
['ATOM     S1 SG2R50 -0.067 !93.531', 'ATOM     C1 CG2R53  0.443 !83.436']

1 Ответ

0 голосов
/ 20 декабря 2018

У вас есть две проблемы, связанные с повторным использованием list s.Во-первых, вы использовали изменяемый аргумент по умолчанию для prefixComments / self.PrefixComments. Не делай этого .Измените инициализатор на:

def __init__(self,atomName="",atomType="",charge=0.0,
             comment="",prefixComments=(),
             verbose=False):
    self.Name=atomName
    self.Type=atomType
    self.Charge=charge
    self.Comment=comment
    self.PrefixComments = list(prefixComments)

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

Во-вторых, ваш __repr__ изменяет этот атрибут, поэтому __repr__ не идемпотентен;он будет расти и расти каждый раз, когда вы его называете.Исправьте это тоже:

def __repr__(self):
    outStrs=self.PrefixComments[:]  # Shallow copy the l
    outStrs.append(
        "ATOM %6s %6s %6.3f !%s"%(
            self.Name,self.Type,self.Charge,self.Comment))
    return ''.join(outStrs)

Примечание: from_line_string действительно должен быть альтернативным конструктором, так что вы можете напрямую использовать его для создания нового экземпляра из строки, а не делать пустой объект только дляпереинициализируйте его на следующей строке.Это легко исправить;просто измените его на classmethod, который анализирует, затем вызывает обычный конструктор (и вызывает исключения при ошибках, вместо использования кодов возврата в стиле C, которые упрощают пропуск ошибок):

# Makes sure this works either on the class or an instance of the class
# as a constructor of a brand new instance
@classmethod
def from_line_string(cls, line):
    # Returns a new instance, or raises an exception if an error is encountered
    lineTokens = line.split()
    if len(lineTokens) < 4:
        raise ValueError("too few entries to construct ATOM record")
    elif lineTokens[0] != "ATOM":
        raise ValueError("atom entries must begin with the keyword 'ATOM'")

    name=lineTokens[1]
    type=lineTokens[2]
    charge=float(lineTokens[3])
    # This works fine, producing an empty string, even if lineTokens is
    # doesn't have an index 5 or higher; comment will be the empty string
    comment = ' '.join(lineTokens[5:]).replace('!', '')
    return cls(name, type, charge, comment)

Это будетупростите использование с:

tempAtom1=AtomEntry()
tempAtom1.from_line_string("ATOM S1     SG2R50 -0.067 !   93.531")

до:

tempAtom1 = AtomEntry.from_line_string("ATOM S1     SG2R50 -0.067 !   93.531")

Возможно, вы также захотите сделать большинство аргументов для __init__ обязательными (без значений по умолчанию, кроме * 1031)* и prefixComment), так как остальные три атрибута являются обязательными, и вам больше не нужно создавать пустые экземпляры только для их повторной инициализации с помощью from_line_string.

...