Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - How to pass an instance variable to method as implicit argument generally and with recursive method

Tags:

I am having an issue passing an instance variable of an object to an instance method.

I have searched for this elsewhere, but all I keep finding is information on how the object is passed to the method using self, which I already know, or just tutorials on general differences between class and instance methods that don't specifically answer my question. The answer to my question definitely exists somewhere, I think I just don't know what to actually ask for.

In my code, I have this class:

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.

Imagine we instantiate an object by doing song = SongData(datapoint). The method xtradata_rm is supposed to search either the song.artist or song.track string for a section in brackets, then if the section found contains any form of the word "featuring" remove that from the string and then try again until no more bracketed expressions with brackets containing "featuring" are found.

I am aware now this is probably 100% the wrong usage of self, but I don't know what to put in its place to achieve the behaviour I want. So then in my script I try to do:

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)

but then I get this error:

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

If I comment out that line, the code runs.

So my first question is, in general, what do I have to do so I can go song.track.example_method() or song.artist.example_method() and get track_name or artist_name printed in the console as expected respectively.

My second question is, how can I do the same with xtradata_rm (i.e. be able to do song.track.xtradata_rm('\((.*?)\)') and essentially insert song.track in place of self within the method), and how does xtradata_rm being recursive and trying to pass the instance variable implicitly to itself within itself change things?

like image 931
Rhyanon Avatar asked Jun 15 '18 11:06

Rhyanon


People also ask

Can we access instance variable in class method Python?

Class methods don't need a class instance. They can't access the instance ( self ) but they have access to the class itself via cls . Static methods don't have access to cls or self . They work like regular functions but belong to the class's namespace.

How do you declare an instance variable in Python?

We can access the instance variable using the object and dot ( . ) operator. In Python, to work with an instance variable and method, we use the self keyword. We use the self keyword as the first parameter to a method.

How do you determine if a variable in a method is an instance variable for the class?

Class Variables are variables that are shared by all instances of a class. So while instance variable can be unique for each instance like our name,email and pay; class variable should be the-same for each instance.

What is class variable and instance variable in Python?

A class variable is a variable that defines a particular property or attribute for a class. An instance variable is a variable whose value is specified to the Instance and shared among different instances. 2. We can share these variables between class and its subclasses. We cannot share these variables between classes.


1 Answers

Looks like you want to add method xtradata_rm to str objects self.artist and self.track.

One thing that you misunderstand about Python is that can't change your object by assigning something to the variable self (or any other variable). self = 123 doesn't change the object, that is behind the name self to 123, it makes the name self point to object 123 (and do it only inside current scope).

To really get this distinction you should watch the talk Facts and Myths about Python names and values by Ned Batchelder.

The other thing is that str objects are immutable, so even if names worked as you expected, you simply could not modify str. For example, bytearray is mutable, and str is not, see the difference:

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'

So to implement your method we need to create wrapper for str object that looks like str and acts like str, but also implements your method. This can be rather hard, for many complicated reasons proxying objects in python is a really involved ordeal.

But fear not! In the dephs of standard library lives a class (144 lines of boring code) just for you: collections.UserString.

All we need to do is to subclass it and implement your method on it:

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

Output:

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

Now you can implement your method on SongAttribute, and change SongData constructor to:

def __init__(self, datapoint):
    self.artist = SongAttribute(datapoint['artist'])
    self.track = SongAttribute(datapoint['name'])
like image 163
Andrew Morozko Avatar answered Sep 28 '22 19:09

Andrew Morozko