1

so right now I am working on a Tesseract class that has a class attribute that calls a function called get_relative_path.

Due to all of the pdf2image and images to string transformation, I save all these files in different folders. But since I don't know the absolute path of all the users I tried to get the relative path depending on the name of the project.

And since I inherit this Tesseract class due to the experimental phase. I want to have the relative base path persistent. I could just implement it in my __init__() But I read in that post that it seems to be more persistent to use class attributes.

The function I wrote is recursive and does what I expect.

class Tesseract():
    @staticmethod
    def get_relpath_to_folder(folder_name, start_path = '.'):
       for root, dirs, files in os.walk(start_path, topdown=True):
          if folder_name in dirs:
             return os.path.relpath(os.path.join(root,folder_name))
       return Tesseract.get_relpath_to_folder(folder_name, os.path.join('..',start_path ))
   
    rel_path = get_relpath_to_folder(folder_name)

My problem is how do I initialize the class variable with a method.

I tried:

  • making the method static

    • TypeError: staticmethod object is not callable
  • making the method class method

    • TypeError: classmethod object is not callable
    • class Tesseract():
          @classmethod
          def get_relpath_to_folder(cls, folder_name, start_path = '.'):
              for root, dirs, files in os.walk(start_path, topdown=True):
                  if folder_name in dirs:
                      return os.path.relpath(os.path.join(root,folder_name))
              return cls.get_relpath_to_folder(folder_name, os.path.join('..',start_path ))
          rel_path = get_relpath_to_folder(folder_name)
      
  • call the method as simple function without any decorators

    • NameError: name get_relpath_to_folder is not defined
    • class Tesseract():
          def get_relpath_to_folder(folder_name, start_path = '.'):
              for root, dirs, files in os.walk(start_path, topdown=True):
                  if folder_name in dirs:
                      return os.path.relpath(os.path.join(root,folder_name))
              return get_relpath_to_folder(folder_name, os.path.join('..',start_path ))
          rel_path = get_relpath_to_folder(folder_name)
      
  • putting the class variable above and below the method

    • NameError: name get_relpath_to_folder is not defined

So what is the best solution here?

I explicitly want to use class variable because that how I make the rel_path persistent for all objects

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • What went wrong with each of the things you tried? – Mad Physicist Sep 22 '21 at 07:45
  • 1
    Can you clarify what you are trying to do? Are you trying to partially apply arguments, i.e. define ``folder_name`` for all calls? – MisterMiyagi Sep 22 '21 at 07:45
  • Could you also fix the indentation? Right now, it's not consistent – Mad Physicist Sep 22 '21 at 07:46
  • If you just want to *call* the "method" inside the class method, it's not a method at all. It's just a function. You don't need any ``@staticmethod`` or ``self`` such, just define and call it as a regular function. – MisterMiyagi Sep 22 '21 at 07:47
  • Does this answer your question? [NameError within class definition](https://stackoverflow.com/questions/57644907/nameerror-within-class-definition) – MisterMiyagi Sep 22 '21 at 07:50
  • 1
    What is the point of having the class `Tesseract` ? – balderman Sep 22 '21 at 08:00
  • 1
    `First:` All of the mentioned methods above threw an error... `Second:` To clarify my work: I am trying to test different Tesseract outputs that's why I created a Tesseract base class that contains all of the specific methods I need. After that, I am applying derivatives for all the different experiments. This might be not the smartest way, but since I want to boost my Object-oriented thinking I am trying to put everything in classes right now. `Third:` Unfortunately, I can not fix the indentation since I don't know how to indent it in the browser. – Konstantin Schuckmann Sep 22 '21 at 09:44

2 Answers2

-1

The class attribute (not a variable) is evaluated when the class is defined. That is most definitely not what you want to do.

Maybe you want to pass in folder_name to the __init__() method and set the attribute there?

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
  • Hey Thanks for the answer, But i have a lot of different folders I want to access. Threfore I pass all specific folders to my methods and not the init. Right now I went with defining the rel_path in my __init__ function: ```python class Tesseract(): def __init__(self): self.rel_path = self.get_relpath_to_folder(folder_name) ``` – Konstantin Schuckmann Sep 22 '21 at 09:35
  • 1
    @KonstantinSchuckmann It sounds like putting it in an instance attribute (like `self.rel_path`) is the correct way here, just as you are doing now. But then you don't need to also pass it to the methods. And likewise, if you pass the folder to the methods, there is no reason for you to store it in the class or instance attribute. But I think that's a worse solution in this case. – Lennart Regebro Sep 22 '21 at 09:58
-2

I don't understand all the context, but your question

how do I initialize the class variable with a method.

can be answered: You could use __class__ to refer to a class of an instance.

class SomeClass:
    def set_class_var_from_instance(self, value):
        self.__class__.class_var = value


some_instance = SomeClass()
another_instance = SomeClass()
some_instance.set_class_var_from_instance(1)
assert another_instance.class_var == 1
assert SomeClass.class_var == 1

Another option is to use a @classmethod on the class and set it on the class:

class SomeClass:
    @classmethod
    def set_class_var(cls, value):
         cls.class_var = value

SomeClass.set_class_var(1)
some_instance = SomeClass()
assert some_instance.class_var == 1

If you want a class variable that is dynamic, but set on the class, you actually want different classes for different scenarios. You can get this by using a factory function to create your class with the class variable:

def factory(value):
     class _SomeClass:
         class_var = value

     return _SomeClass

SomeClassWithOne = factory(1)
some_instance_with_one = SomeClassWithOne()
assert SomeClassWithOne.class_var == 1
assert some_instance_with_one.class_var == 1
Jonathan Scholbach
  • 4,925
  • 3
  • 23
  • 44
  • I get this point but what I try to achieve is that the class attribute is initialized with the class and present from beginning – Konstantin Schuckmann Sep 22 '21 at 09:51
  • @KonstantinSchuckmann But a class attribute will be the same for all instances of that class, That means you can only access one folder per run of the software. That's again absolutely not what you want to do at all. – Lennart Regebro Sep 22 '21 at 09:56
  • @KonstantinSchuckmann If this is what you want, why do you need to set it in a method? Just write it on the class – Jonathan Scholbach Sep 22 '21 at 10:16
  • @KonstantinSchuckmann Update my answer, it is really hard to understand what you want - but maybe this is it? – Jonathan Scholbach Sep 22 '21 at 10:21
  • @LennartRegebro Yes that is exactly what I want. Since the relative Path does not realy change. I am using spyder IDE and VSCode both have different behaviour when it comes to access folders somehow. Now I want to have the relative path for my base directory and from that point on i build up my accessing folder structure. This structure can change during the devolpment phase and i dont want to have all paths hardcoded in my script. Thats why I want the class attribute to be the same for all instances. Alongside with os.path.join I build my structure arround the relative paths. – Konstantin Schuckmann Sep 22 '21 at 10:33
  • @KonstantinSchuckmann "Since the relative Path does not realy change." - Weird, you just said that you have loads of paths you want to open. If the path is the same throughout the whole program run, then put it in a global configuration object. Or as an instance attribute. Maybe an "args" variable, if it's a command line. Don't you have one `Tesseract()` instance per tesseract file? – Lennart Regebro Sep 22 '21 at 17:04