简体   繁体   中英

Variable behaviour in python - making more efficient code

Trying to do some optimization here on a class. We're trying not to change too much the class definitions. In essence we are instantiating a ClassA N times but one of the methods has a nasty file read.

for x in range(0, N):
  cl = ClassA()
  cl.dostuff(x)

The class looks like this:

class ClassA:
  def dostuff(self, x):
        #open nasty file here  
        nastyfile = open()
        do something else

We could bring that file read out of the class and put in before the loop as the file will not change. But is there a way we can ensure that we only ever open the nasty file once for instances of the class. Ie so for example on the first instantiate of the class it is defined for all future instances of the class without having to read in again. Is there a way to do this in the current form without really changing the structure too much of the existing code base.

One question relates to the interpreter - ie is python smart enough to cache variables just as nastyfile, so that we do as we are, or is the quick and dirty solution the following:

nastyfile = open()
for x in range(0, 1):
      cl = ClassA()
      cl.dostuff(x)

Looking for a pythonic way to do this.

You could encapsulate opening the file in a classmethod .

class ClassA():

    @classmethod
    def open_nasty_file(cls):
        cls.nasty_file = open('file_path', 'file_mode')

    def do_stuff(self):
        if not hasattr(self, 'nasty_file'):
            self.open_nasty_file()

This approach relies on the fact that attribute look-ups will try finding the attribute on the class if not found on the instance.

You could put this check/instantiation in the __init__ function if you want it opened when the first instance is instantiated.

Note that this method will leave the file open, so it will need to be closed at some point.

You could have a class method that opens the file when the first instance asks for it. I've wrapped it in a lock so that it is thread safe.

import threading

class ClassA:

  _nasty_file = None
  _nasty_file_lock = threading.Lock()

  def dostuff(self, x):
        #open nasty file here  
        nastyfile = get_nasty_file()
        do something else

  @classmethod
  def get_nasty_file(cls):
    with cls._nasty_file_lock:
        if cls._nasty_file is None:
            with open('nastyfile') as fp:
                cls._nasty_file = fp.read()
    return cls._nasty_file

Instances can access and modify class attributes by themselves. So you can just set up an attribute on the class and provide it with a default (None) value, and then check for that value before doing anything in dostuff . Example:

class A():
    nastyfileinfo=None
    def dostuff(self,x):
        if A.nastyfileinfo: print('nastyfileinfo already exists:',A.nastyfileinfo)
        if not A.nastyfileinfo:
            print('Adding nastyfileinfo')
            A.nastyfileinfo='This is really nasty' ## open()
            print('>>>nastyfileinfo:',A.nastyfileinfo)
        ## Continue doing your other stuff involving x

for j in range(0,10):
    A().dostuff(j)

nastyfileinfo is also considered an attribute of the instance, so you can reference it with instance .nastyfileinfo, however if you modify it there it will only update for that one specific instance, whereas if you modify it on the class, all other instances will be able to see it (provided they didn't change their personal/self reference to nastyfileinfo).

instants=[]
for j in range(0,10):
    instants.append(A())

for instance in instants:
    print(instance.nastyfileinfo)

instants[5].dostuff(5)

for instance in instants:
    print(instance.nastyfileinfo)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM