Python - 通常如何使用递归方法将实例变量作为隐式参数传递给方法

Rhy*_*non 5 python regex python-3.x

我在将对象的实例变量传递给实例方法时遇到问题。

我已经在其他地方搜索过这个,但我一直找到的只是有关如何使用我已经知道的将对象传递给方法的信息self,或者只是关于类和实例方法之间的一般差异的教程,这些差异没有具体回答我的问题。我的问题的答案肯定存在于某个地方,我想我只是不知道实际要问什么。

在我的代码中,我有这个类:

class SongData:

    def __init__(self, datapoint):
        self.artist = datapoint['artist']
        self.track = datapoint['name']

    def xtradata_rm(self, regex, string=None):
        if string is None:
            string = self
        srchrslts = re.search(regex, string)
        if srchrslts is not None:
            if regex == 'f.*?t':
                self = self.replace(string,'')
                self.xtradata_rm('\((.*?)\)')
            else:
                self.xtradata_rm('f.*?t', srchrslts)

    def example_method(self):
        #This one isn't actually in the code, included for ease of explanation.
        print(self) 

    #some more methods irrelevant to question down here.
Run Code Online (Sandbox Code Playgroud)

想象一下我们通过执行以下操作来实例化一个对象song = SongData(datapoint)。该方法xtradata_rm应该在 或song.artist字符串中搜索song.track括号中的部分,然后如果找到的部分包含任何形式的单词“featuring”,则将其从字符串中删除,然后重试,直到不再有括号内的表达式包含“featuring”被发现。

我现在知道这可能是 100% 错误的 self 用法,但我不知道应该用什么来代替它来实现我想要的行为。那么在我的脚本中我尝试这样做:

file_list = glob.glob("*procData.json")


for datafname in file_list:
    datafile = json.load(open(datafname))

    for i, datapoint in enumerate(datafile['EnvDict']):
        song = SongData(datapoint)
        song.track.xtradata_rm('\((.*?)\)')
        song.releasefetch(lfmapi)
        song.dcsearcher(dcapi)
        datapoint.update({"album": song.release, "year": song.year})

    with open("upd" + datafname, 'w') as output:
        json.dump(datafile, output)
Run Code Online (Sandbox Code Playgroud)

但后来我得到这个错误:

Traceback (most recent call last):
    song.track.xtradata_rm('\((.*?)\)')
AttributeError: 'str' object has no attribute 'xtradata_rm'
Run Code Online (Sandbox Code Playgroud)

如果我注释掉该行,代码就会运行。

所以我的第一个问题是,一般来说,我必须做什么才能分别按预期在控制台中进行song.track.example_method()song.artist.example_method()获取track_name或打印。artist_name

我的第二个问题是,我怎样才能做同样的事情xtradata_rm(即能够在方法内执行song.track.xtradata_rm('\((.*?)\)')并基本上插入),以及递归并尝试将实例变量隐式传递给自身会如何改变事情? song.trackselfxtradata_rm

And*_*zko 4

看起来您想xtradata_rmstr对象添加方法self.artistself.track

您对 Python 的误解之一是,不能通过向变量self(或任何其他变量)分配某些内容来更改对象。不会更改名称后面的self = 123对象,它使名称指向对象(并且仅在当前范围内执行此操作)。self123self123

要真正了解这种区别,您应该观看 Ned Batchelder 的演讲《关于 Python 名称和值的事实与神话》

另一件事是str对象是不可变的,因此即使名称按您的预期工作,您也无法修改str. 例如,bytearrayis mutable 和stris not,看看区别:

In [1]: b = bytearray(b'My example string')

In [2]: id(b)
Out[2]: 4584776792

In [3]: b[3:10] = b'modified'

In [4]: b
Out[4]: bytearray(b'My modified string')

In [5]: id(b) # same object
Out[5]: 4584776792

In [6]: s = 'My example string'

In [7]: s[3:10] = 'modified'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-22fe89ae82a3> in <module>()
----> 1 s[3:10] = 'modified'

TypeError: 'str' object does not support item assignment

In [8]: new_s = s.replace('example', 'modified')

In [9]: id(new_s) # different object
Out[9]: 4584725936

In [10]: id(s)
Out[10]: 4584762296

In [11]: s # original string unmodified
Out[11]: 'My example string'
Run Code Online (Sandbox Code Playgroud)

因此,为了实现您的方法,我们需要为str看起来像str和行为类似的对象创建包装器str,但也实现您的方法。这可能相当困难,由于许多复杂的原因,在 python 中代理对象是一个非常复杂的考验

但不要害怕!在标准库的深处有一个专门为你设计的类(144行无聊的代码):collections.UserString

我们需要做的就是对其进行子类化并在其上实现您的方法:

class SongAttribute(collections.UserString):
    def example_mutate(self):
        """Works UNLIKE other string methods, mutates SongAttribute object,
        but I think this is how you want your code to work. Jugging just a bit ;) 
        Note: actual str object still is immutable and wasn't mutated,
        self.data now just references another immutable str object.

        P.S.: self.data is the object being proxied by UserString class
        """

        self.data = self.data.replace(' ', '_')
        return self

    def example_return_new(self):
        """Works like all other string metods, returns new string"""
        return self.replace(' ', '_')

song = SongAttribute('My Song Name') # creating new song attribute (artist or track)
print(song, type(song)) # it looks like str, but isn't
print(song.upper(), type(song.upper())) # it has all of the str methods, but they return SongAttribute objects, not str objects.

# Return new
print()
new_song = song.example_return_new()
print(new_song, type(new_song)) # we got underscored SongAttribute

# Mutate
print()
print(song, type(song))
print(song.example_mutate(), type(song.example_mutate())) # this method changed song object internally
print(song, type(song)) # and now we still see the changes
Run Code Online (Sandbox Code Playgroud)

输出:

My Song Name <class '__main__.SongAttribute'>
MY SONG NAME <class '__main__.SongAttribute'>

My_Song_Name <class '__main__.SongAttribute'>

My Song Name <class '__main__.SongAttribute'>
My_Song_Name <class '__main__.SongAttribute'>
My_Song_Name <class '__main__.SongAttribute'>
Run Code Online (Sandbox Code Playgroud)

现在您可以在 上实现您的方法SongAttribute,并将SongData构造函数更改为:

def __init__(self, datapoint):
    self.artist = SongAttribute(datapoint['artist'])
    self.track = SongAttribute(datapoint['name'])
Run Code Online (Sandbox Code Playgroud)