【问题标题】:Python - How to pass an instance variable to method as implicit argument generally and with recursive methodPython - 如何将实例变量作为隐式参数传递给方法并使用递归方法
【发布时间】:2018-06-15 11:50:31
【问题描述】:

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

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

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

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.

假设我们通过song = SongData(datapoint) 实例化一个对象。方法xtradata_rm 应该在song.artistsong.track 字符串中搜索括号中的部分,然后如果找到的部分包含任何形式的“功能”一词,则从字符串中删除它,然后重试直到没有发现更多括号中包含“特征”的括号表达式。

我现在知道这可能是 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)

然后我得到这个错误:

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

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

所以我的第一个问题是,一般来说,我该怎么做才能去song.track.example_method()song.artist.example_method() 并按预期在控制台中分别打印track_nameartist_name

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

【问题讨论】:

  • 您的代码的问题是您试图将song.track 作为self 传递给xtradata_rm 方法。相反,当您将其调用为 song.xtradata_rm('\((.*?)\)') 时,请在 xtradata_rm 方法中使用 self.track

标签: python regex python-3.x


【解决方案1】:

您似乎想将方法 xtradata_rm 添加到 str 对象 self.artistself.track

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

要真正获得这种区别,您应该观看 Ned Batchelder 的演讲 Facts and Myths about Python names and values

另一件事是str 对象是不可变的,因此即使名称按预期工作,您也无法修改str。比如bytearray是可变的,而str不是,看看区别:

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'

因此,要实现您的方法,我们需要为 str 对象创建包装器,该对象看起来像 str,行为类似于 str,但也实现了您的方法。这可能相当困难,由于许多复杂的原因,在 python is a really involved ordeal 中代理对象。

但不要害怕!在标准库的深处有一个专门为您服务的类 (144 lines of boring code):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

输出:

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'>

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

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

【讨论】:

猜你喜欢
  • 2018-01-23
  • 2012-03-15
  • 2010-09-25
  • 2012-07-12
  • 2018-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-19
相关资源
最近更新 更多