Python Forum

Full Version: Python class members based on a type value
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi, I am curios whether it is possible to have a class define specific members based on a "type". I want to implement something like this:

enum type {
    REQUEST_A = 1,
    REQUEST_B = 2
};

struct packet {
    enum type type;
    union {
         struct a_args_s {
               int arg1;
         } a_args;
         struct b_args_s {
               int arg1;
               int arg2;
         } b_args;
     };
};
The enum type can be done with enum.IntEnum but I am not sure how to do the struct packet. I know it could be done with ctypes but I want to do with with pure Python.

I could think of something with the __setattr__() method, where only the correct attributes are allowed to be set but I was wondering if there was a better way?
In Python I would create a base class and two subclasses
from abc import ABC

class Packet(ABC):
    pass

class APacket(Packet):
    def __init__(self, arg1: int):
        self.arg1 = arg1

class BPacket(Packet):
    def __init__(self, arg1: int, arg2: int):
        self.arg1 = arg1
        self.arg2 = arg2
(Apr-07-2025, 04:54 PM)Gribouillis Wrote: [ -> ]In Python I would create a base class and two subclasses

Yes, I thought of that too. But that does not achieve the goal because you have *two* different classes, not one class that has different members like the struct.
Could you elaborate in plain English what you want to achieve? For those not familiar with c++
I don't understand how "to have a class define specific members based on a "type"" relates to example. I see union of two methods with one/two arguments, but not different types (as I said, I am not familiar with c++)
As I understand it so far functools.singledispatchmethod() may be what you look for https://docs.python.org/3/library/functo...atchmethod
(Apr-07-2025, 05:38 PM)buran Wrote: [ -> ]Could you elaborate in plain English what you want to achieve? For those not familiar with c++
I don't understand how "to have a class define specific members based on a "type"" relates to example. I see union of two methods with one/two arguments, but not different types (as I said, I am not familiar with c++)
As I understand it so far functools.singledispatchmethod() may be what you look for https://docs.python.org/3/library/functo...atchmethod

The structure that I described can hold two different "packet" types. I can transfer the structure over a socket and on the receiving end decide what packet type it is based on the common type member. Based on that type member I know what other members are valid.

singledisplatchmethod is pretty close but it works on methods, not members.
(Apr-07-2025, 05:05 PM)voidtrance Wrote: [ -> ]But that does not achieve the goal because you have *two* different classes, not one class that has different members like the struct.
The goal is far from clear. With my design, an instance of Packet can be either an instance of APacket or BPacket. You can still define functions that take Packet arguments and do whatever you want. You can also use polymorphism to implement methods that work transparently differently for APackets or BPackets.

Perhaps you could show us examples of how you want to use the type and why it would be better to have something that resembles a C union. The reason for the existence of C unions is mainly that C doesn't have classes and also that C structures are a very thin syntactic layer above the physical memory.
it looks like you try to write python code with C paradigm in mind and that is the problem

Maybe having two classes and singledispatch function on receiving end to process what you get would do..
If your interest is sending packets over a socket you should look at struct.

A literal interpretation of the C code.
import struct
from enum import IntEnum


class Request(IntEnum):
    REQUEST_A = 0
    REQUEST_B = 1


class Packet:
    
    def __init__(self, request: Request, arg1: int, arg2: int = 0) -> None:
        self.type = request
        self.arg1 = arg1
        self.arg2 = arg2

    def as_bytes(self):
        if self.type == Request.REQUEST_A:
            return struct.pack(">II", self.type, self.arg1)
        return struct.pack(">III", self.type, self.arg1, self.arg2)


print(Packet(Request.REQUEST_A, 42).as_bytes().hex())
print(Packet(Request.REQUEST_B, 42, 7).as_bytes().hex())
Output:
000000000000002a 000000010000002a00000007
If the only difference between the packet types is the number of arguments. I would do this:
import struct


class Packet:

    def __init__(self, *args) -> None:
        self.args = args

    def as_bytes(self):
        return struct.pack(f">I{len(self.args)}I", len(self.args), *self.args)


print(Packet(42).as_bytes().hex())
print(Packet(42, 7).as_bytes().hex())
Output:
000000010000002a 000000020000002a00000007