0

I'm just beginning my adventure with Python 2.7.

I want to make some kind of strong typed reference from one class to another.

Lets say I have two classes:

class Module:
    count = 0

    def __init__(self, m_name, m_type, m_on=False):
        self.name  = m_name
        self.type = m_type
        self.on = m_on
        self.id  = self.count
        self.count += 1

And Slot which I want to hold Module as element:

class Slot:
    count = 0

    def __init__(self):
        self.id  = self.count
        self.count += 1
        self.element = None

Now I want to add method to set element in that slot and then use it in other methods (with it fields and methods) but I want to make sure that it will be element : Module.

It will be also helpful if I can "tell" PyCharm that element is type of Module so it will auto-complete my typing.

Is it even possible? Can I trick python other then:

self.element = Module()

in init method (if I do above, it recognize element as type of Module and it does auto-complete).

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
bart.z33
  • 139
  • 1
  • 2
  • 12

2 Answers2

1

As haraprasadj says, Python is not statically typed so you should probably try to get used to that and code accordingly. Python is NOT Java. I came from Java, and it took me a while getting used to this kind of stuff, but once you do, you'll find Java's syntax unnecessarily "picky" and cluttered (well... at least, I do now)

All this said, with new style classes (classes inheriting from object) you can use the @property decorator and create transparent getters and setters (see this question, this question and this document)

In your case, to enforce that the element attribute of the Slot class is always None or an instance of Module, you can do the following:

class Module(object):
    count = 0

    def __init__(self, m_name, m_type, m_on=False):
        self.name  = m_name
        self.type = m_type
        self.on = m_on
        self.id  = self.count
        self.count += 1

class Slot(object):
    count = 0

    def __init__(self):
        self.id  = self.count
        self.count += 1
        self.element = None

    @property
    def element(self):
        return self._element

    @element.setter
    def element(self, e):
      if not(e is None or isinstance(e, Module)):
          raise TypeError("Element must be None or Module")
      self._element = e

if __name__ == "__main__":
    s = Slot()
    m = Module('name', 'type')
    print "s.element? %s" % s.element
    s.element = m
    print "s.element? %s" % s.element
    # Now, exception:
    s.element = "hellouuuu"

Which outputs:

s.element? None
s.element? <__main__.Module object at 0x26c9b10>
Traceback (most recent call last):
  File "./stack13.py", line 35, in <module>
    s.element = "hellouuuu"
  File "./stack13.py", line 25, in element
    raise TypeError("Element must be None or Module")
TypeError: Element must be None or Module

Ok, what's happening here? When you create an instance of Slot (variable s) and you try to assign something to its module attribute using s.module = "whatever", what's actually happening is that the @element.setter method of s is called, with "whatever" as the e parameter. If the method doesn't throw an exception, the actual attribute is stored in s._module) You can examine the actual attributes of the class using vars(s) (see this about vars):

>> s = Slot()
>> m = Module('name', 'type')
>> s.element = m
>> print "%s" % vars(s)
{'count': 1, 'id': 0, '_element': <__main__.Module object at 0x19d0b10>}

See? Thre's no element there, but _element

Now, when you try to read the element attribute, using s.element, what's happening is that the method decorated with @property is being called.

In the example I posted, add a print statement in that method:

@property
def element(self):
    print "I'm reading self._element. Wohooo"
    return self._element

Now, re-execute the script. You'll see this in the output:

I'm reading self._element. Wohooo
s.element? None
I'm reading self._element. Wohooo
s.element? <__main__.Module object at 0x1fd0ad0>
{'count': 1, 'id': 0, '_element': <__main__.Module object at 0x1fd0ad0>}
Traceback (most recent call last):
  File "./stack13.py", line 37, in <module>
    s.element = "hellouuuu"
  File "./stack13.py", line 26, in element
    raise TypeError("Element must be None or Module")
TypeError: Element must be None or Module
Community
  • 1
  • 1
Savir
  • 17,568
  • 15
  • 82
  • 136
0

I guess you mean static typed reference, not strong typed reference. Python references ARE strong typed.

If you want static typed, Python is not the way to go.

I don't also think there is any trickery in the statement:

self.element = Module()

This just creates an object of class Module. Also the type of variable 'element' remains Module class unless you do an explicit reassignment to another type.

This is different from what we do in C++ or Java, but this flexibility adds to the power of Python in my opinion

Hope this helps.

haraprasadj
  • 1,059
  • 1
  • 8
  • 17
  • I forgot to say I want to avoid calling "constructor" - as you can see it assigns ID. Can I have many __init__ methods? – bart.z33 Apr 06 '14 at 10:36
  • From what I understand, you can create another method in the Slot class (say, set_element) which takes a Module as input parameter. Or , if you want element to be set the moment Slot is created, you can add an input parameter to Slot's constructor to accept a Module and set that value to element. If you want to do something else entirely, I am sorry, my knowledge is also limited – haraprasadj Apr 06 '14 at 10:47