簡體   English   中英

在python 2.7中創建新的不同類實例時出現問題

[英]Problem creating new distinct class instance in python 2.7

我有一個本質上應該能夠初始化自身以及基於一行文本(字符串)設置內部值的類。 這在創建單個實例時似乎很好用,但是,在創建第二個實例時,饋入第一個實例的文本行顯然會饋入第二個實例的內部變量之一! 該類的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']

您有兩個問題,都與重用list有關。 一種,您對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__大多數參數成為強制性的(除了commentprefixComment之外,沒有默認值),因為其他三個屬性是必需的,並且您不再需要使空實例僅通過from_line_string進行初始化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM