-1

I know that python used duck typing, but I was wondering whether it is possible to enforce type validation for class variables, __slots__ in particular.

for example -

class Student:
def __init__(self, name):
    self.name = name

class Class:
    __slots__ = ('class_representative',
                 'var_2',
                 'var_3',
                 '...',  # Assume many more variables below
                )

    def __init__(self, *args, **kwargs):
        self.class_representative = kwargs.get('cr')
        self.var_2 = kwargs.get('v2')
        self.var_3 = kwargs.get('v3')
        ... # Assume many more variables below

In the above example, how do I make sure that whenever any object gets assigned to the class_representative variable, it should always be of type Student?

Is something like the following possible?

class Class:
    __slots__ = ('class_representative': Student,
                 'var_2',
                 'var_3',
                 '...',  # Assume many more variables below
                )
aaryan
  • 2,229
  • 2
  • 10
  • 15
  • 1
    You have to write the code to enforce that yourself... not sure what the relevance of `__slots__` is – juanpa.arrivillaga Mar 28 '21 at 04:31
  • Are you asking about a static typing check that the attribute only gets `Student` values, or a runtime check? – Blckknght Mar 28 '21 at 06:07
  • @Blckknght I want to prevent assigning any other types than `Student` to `class_representative` variable at any point in the life of program execution. – aaryan Mar 28 '21 at 09:56
  • @aaryan: that didn't really answer my question. Static type checking happens *before* you run your program, and seeks to prove that you never call the function with the wrong type. Runtime checking happens while the program runs. They're implemented completely differently, so you really need to pick which one you want and do that thing. `__slots__` has nothing to do with type checking (of either kind). It's purpose is to help you to save memory for objects you are going to create huge numbers of by avoiding the need for a `__dict__` in each instance. – Blckknght Mar 28 '21 at 10:43
  • @Blckknght Sorry for not understanding your question correctly earlier. Yes, I want static type checking here. There is a way that `Silvio Mayolo` has answered below but it doesn't work with *args/**kwargs syntax. See my comment there for more details. – aaryan Mar 28 '21 at 10:52

1 Answers1

3

When you say "static typing", I assume you're referring to PEP 484. In that case, __slots__ makes absolutely no difference, and we annotate the type of instance variables the way we always do for Python classes.

class Class:
    __slots__ = ('class_representative',)
    class_representative: Student

    def __init__(self, student: Student) -> None:
        self.class_representative = student

Incidentally, if you're going for static type checking in Python (which I highly recommend; it's a surprisingly well-designed system), taking and forwarding *args and **kwargs in your constructor is an excellent way to lose any static verifiability. Take the arguments you need, with as explicit of types as you can manage to provide, and forward what you have to.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • In some places, I have a lot of arguments to pass to the constructor. It makes it look very ugly if I write arguments the way you have suggested. Hence I am using `**kwargs`, but then I lose static verifiability. So I was wondering if there is a way I can specify them in `__slots__` somehow. – aaryan Mar 28 '21 at 09:54
  • 1
    @aaryan: I'm not sure `**kwargs` does what you think it does. In your example code, your `__init__` method is the same as not having one at all. If you don't implement `__init__`, you'll inherit `object.__init__`, which accepts no arguments other than `self` and does nothing. Your code accepts arbitrary arguments, but passes them on to `object.__init__`, which will raise an exception if there are any arguments at all (of any types). Nothing in your class will set up `class_representative` based on any of the arguments in `args` or `kwargs`, so I'm not sure what you want to type check. – Blckknght Mar 28 '21 at 10:59
  • @Blckknght I have improved the sample example in question to make it much easier to understand the problem. – aaryan Mar 28 '21 at 11:04
  • I still agree with @Blckknght. If you want to set the instance variable `class_representative` to the argument `cr`, then just take an argument called `cr` and use it. `**kwargs` is for when you want to accept all keyword arguments as a dictionary. It's entirely pointless *and* costs you static verifiability if you know what arguments to expect. – Silvio Mayolo Mar 28 '21 at 15:39
  • Also, it's worth pointing out, just to make sure there's no misunderstanding. `__slots__` is primarily an efficiency trick to make the class size smaller. It's not by any means necessary for a class in Python. So unless this class is being used in an extreme bottleneck situation (and you've benchmarked it and discovered proof of that), then `__slots__` is probably premature. In particular, it's not required in classes and the vast majority of Python classes never specify it – Silvio Mayolo Mar 28 '21 at 15:40