我有一个本质上应该能够基于一行文本(字符串)初始化自身和设置内部值的类。 这在创建单个实例时似乎很好用,但是,在创建第二个实例时,馈入第一个实例的文本行显然会馈入第二个实例的内部变量之一! 该类的 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']
答案 0 :(得分:3)
您遇到了两个问题,都与重用list
有关。一,您为prefixComments
/ self.PrefixComments
使用了可变的默认参数。 Don't do that。将初始化程序更改为:
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__
的大多数参数设为强制性的(除了comment
和prefixComment
之外,没有其他默认值),因为其他三个属性是必需的,并且您不再需要创建空实例,只需使用from_line_string
重新初始化它们即可。