plover
plover

Reputation: 449

How to make sure a class function wont be called until another class function has been called first?

I have a class object that creates some data fields:

class DataFields(object):

   _fields_ = ['field_1', 'field_2', 'data_length']

    def __init__(self, data=None):
        if data != None:
           self.create_fields(data)

    def create_fields(self, data):
        i = 0
        for field in self._fields_:
            setattr(self, field, data[i])
            i += 1

    def get_datalength(self):
        return self.data_length

What is the best way to make sure that the get_datalength() function cannot be called unless the data_length field has been created (that is, unless the create_fields() function has been called once).

I've thought about either using a variable that gets initialized in the create_fields and is checked in get_datalength() or try-except inside the get_datalength() function. What is the most Pythonic (or the best) way?

Upvotes: 4

Views: 170

Answers (4)

user966588
user966588

Reputation:

This can be solved by having a dictionary, as a class variable, with method names as keys.

called['method1'] 
called['method2']
called['method3']
...

And setting the key in that method call

class SomeClass(obj):
    def method1():
        called['method1'] = 1
    def method2():
        if method1 in called:
            # continue

Upvotes: 0

ProfHase85
ProfHase85

Reputation: 12253

I think the most pythonic way would be to throw an exception:

def get_datalength(self):
    try:
        return self.data_length
    except AttributeError:
        raise AttributeError("No length call create_fields first")

Simple reason: There is no way to prevent the user to call this function on the object. Either the user would get a AttributeError and would not understand what is going on, or you provide an own Error class or at least error message.

BTW: It is not pythonic creating getter methods(there are no such things as 'private members') If you need to do smaller operation on the value returning it have a look at the @property decorator

@property
def datalength(self):
   return do_some_stuff(self.data_length)

Upvotes: 4

pfc
pfc

Reputation: 310

Using an exception is probably the best way for what you are doing however there are alternatives that may be useful if you will be using this object from an interactive console:

def fn2(self):
    print("this is fn2")

class test:
    def fn1(self):
        print("this is fn1")
        self.fn2 = fn2
    def fn2(self):  # omit this if you want fn2 to only exist after fn1 is called
        print("Please call fn1 first")

I wouldn't recommend this for every-day use but it can be useful in some cases. If you omit defining fn2 within the class, then the method fn2 will only be present after fn1 is called. For easier code maintenance you can do the same thing like this:

class test:
    def fn1(self):
        print("this is fn1")
        self.fn2 = self._fn2
    def _fn2(self):
        print("this is fn2")
    def fn2(self):  # omit this if you want fn2 to only exist after fn1 is called
        print("Please call fn1 first")

If this is to be used inside a module that will be imported then you should either raise an exception or return a valid value like the other answers have suggested.

Upvotes: 0

ndpu
ndpu

Reputation: 22571

By using getattr with default value, you can return None or any value if there is no data_length attribute yet in instance:

def get_datalength(self):
    return getattr(self, 'data_length', None)

Upvotes: 1

Related Questions