Search code examples
pythonpython-3.xoop

How to design python class so that i can return a value instead of address when using just the instance name without dot?


I have a header file which describes the format of binary data stored. This file has fields like DTYPE for datatype, NMEMB for number of members, NFILE for number of files etc.

In python i am parsing the header and using class structure to easily access them later.

class Header:
    def __init__(self,path:str):
        self.DTYPE = foo1() #parsing DTYPE, typical value: '<f8'
        self.NMEMB = foo2() #parsing NMEMB, typical value: an integer
        self.NFILE = foo3() #parsing NFILE, typical value: an integer

Later in other files i can use it as

h = Header("\path\to\header.txt")
nfile = h.NFILE

The DTYPE has further substructures.

  • The first character represents endianness or byte order (< for little endian, > for big endian and = for machine endian).
  • The second character represent datatype (f for float, i for integer).
  • The third character represent byte width (f8 means 8-byte float or double, f4 means 4-byte float)

So to further access them, i used another class representation.

class _DTYPE:
    def __init__(self,dtype:str):     # input dtype = '<f8'
        self.rawString  = dtype       # get '<f8'
        self.endianness = dtype[0]    # get '<'
        self.character  = dtype[1]    # get 'f'
        self.bytewidth  = dtype[2]    # get '8'

class Header:
    def __init__(self,path:str):
        self.DTYPE = _DTYPE(foo1())   # foo1() returns '<f8'
        self.NMEMB = foo2()
        self.NFILE = foo3()

Later i use it as

h = Header("\path\to\header.txt")
char  = h.DTYPE.character    # returns 'f'
width = h.DTYPE.bytewidth    # returns '8'
raw   = h.DTYPE.rawString    # returns '<f8'

However, for better code structure i want to get the raw string with the following syntax for user.

raw  = h.DTYPE   # It returns reference to object. i want to get '<f8'. 

But python returns the address for _DTYPE object as expected like "<main._DTYPE object at 0x7f5742dcbe50>".

QUESTION Is it possible to wire python class in a way that if i don't use dot notation it should give me a value and if i use dot notation it should switch to class object and give me access to further subclass members?

This is possible in other languages like C#. Also python does similar behaviours internally, as everything in python is an object. For example say in a string.

text="Some string"
a=text
b=text.upper()

Here i can use just text to get the value it holds (not reference to/address of some str object) and i can also use dot notation to access members of the str class instanced as text.

Is there any magic methods for this purpose? If not is it probable that they add this feature - "Give a magic method to return something instead of class address when not using dot."

Appreciate your help. Thanks for your time :)

EDIT 1: The suggested magic functions like __str__ or __repr__ only works for print() function. How to get that to a variable for further processing or what if i would like to return a integer when not using dot.

EDIT 2: for clarification, I want the user to only type syntax h.DTYPE to get the raw string as <f8 (first goal). Or if user wants, user can access further processed valued by using dot notation as h.DTYPE.character (second goal). The implementation i mentioned to use another class solved the second goal at the cost of first goal. The former syntax now returns address instead of raw text.


Solution

  • I'm going to paraphrase your question. If this is not what you seek, let me know.

    Problem:

    In some cases I would like to get an instance field value back when I reference the instance. I recognize that many time I want to get the address of the instance so I can reference members of the instance or copy it or what have you. Specifically, in certain situations, I would like:

    raw  = h.DTYPE
    

    To set raw to the value of a selected instance member rawString. However, it is also critical that I can still reference instance members like:

    raw  = h.DTYPE.rawString
    

    Solution:

    The way I would likely proceed is by overriding __call__(). This will allow you to do "something" if your instance variable is "called" as if were a method like:

    raw  = h.DTYPE()
    

    Here is a proof of concept:

    class _Dtype:
        def __init__(self,dtype:str):     # input dtype = '<f8'
            self.rawString  = dtype       # get '<f8'
            self.endianness = dtype[0]    # get '<'
            self.character  = dtype[1]    # get 'f'
            self.bytewidth  = dtype[2]    # get '8'
    
        def __call__(self):
            return self.rawString
    
    class Header:
        def __init__(self,path:str):
            self.DTYPE = _Dtype("<f8")
            self.NMEMB = "foo"
            self.NFILE = "bar"
    
    header = Header("")
    test = header.DTYPE()            ## <--- Calling your instance
    test2 = header.DTYPE.rawString   ## <--- Referencing your instance
    print(test, test2)
    

    This will set test and test2 to "<f8" and then print them giving you:

    <f8 <f8