-3

I don’t understand how to do assignment checking to a variable, as it is done in Kivy. I know how this is done for class properties, and it looks like this

#!/usr/bin/python3.6    
class Foo:
   var = property()
   def __init__(self):
      self._var = 0
   @var.setter
   def var(self, value):
      self._var = value
      # code setter
      pass

   @var.getter
   def var(self):
      # code getter
      print('Getter method')
      return self._var

a = Foo()
a.var = 5
print(a.var)
# Getter method
# 5    

In Kivy can do:

class LabelBase(Label):
msg = StringProperty('t')

   def __init__(self, **kwargs):
       super(LabelBase, self).__init__(**kwargs)
       self.msg = 5

I take

     Traceback (most recent call last):
   File "/home/Python/Prj/View/Main.py", line 83, in <module>
     Start().build()
   File "/home/Python/Prj/View/Main.py", line 73, in build
     GUI().run()
   File "/usr/lib/python3/dist-packages/kivy/app.py", line 800, in run
     root = self.build()
   File "/home/Python/Prj/View/Main.py", line 65, in build
     main_window = MainFrame()
   File "/home/Python/Prj/View/Main.py", line 52, in __init__
     self.label = LabelBase(text='test')
   File "/home/Python/Prj/View/Main.py", line 16, in __init__
     self.msg = 5
   File "kivy/properties.pyx", line 483, in 
   kivy.properties.Property.__set__
   File "kivy/properties.pyx", line 521, in kivy.properties.Property.set
   File "kivy/properties.pyx", line 512, in kivy.properties.Property.set
   File "kivy/properties.pyx", line 678, in 
   kivy.properties.StringProperty.check
   ValueError: LabelBase.msg accept only str

Renamed the question because it did not correspond to what was happening

Shatten
  • 13
  • 3
  • 1
    Assignment isn't a method. – PM 2Ring Nov 23 '18 at 08:32
  • Good, how was it done team Kivy? – Shatten Nov 23 '18 at 08:34
  • 1
    I doubt very much that this is possible even in Kivy. Please show an example where you do that. – Daniel Roseman Nov 23 '18 at 08:36
  • example in topic – Shatten Nov 23 '18 at 08:38
  • This makes little to no sense, please take a moment to understand that. What good does doing it this way do? – Silver Nov 23 '18 at 08:38
  • 2
    But isn't that exactly the same as what you have in your first snippet above, with getter and setter methods? It's not at all what you do in the second, where you try and reassign the whole object. – Daniel Roseman Nov 23 '18 at 08:41
  • That is entirely different. That's assignment INSIDE the class, and has nothing to do with reassigning a variable. – Silver Nov 23 '18 at 08:42
  • ok, why self.msg = 5 does not override the reference, but calls the validation method in the StringProperty class? – Shatten Nov 23 '18 at 08:49
  • Because there's a setter method. **Exactly** as you have in your first snippet. – Daniel Roseman Nov 23 '18 at 08:50
  • If I understand correctly, self.msg = StringProperty () creates an instance of the StringProperty class, if I do self.msg = 5, I have to redefine the object variable by a number, but the function call occurs, the StringProperty class is wrapped in property? – Shatten Nov 23 '18 at 08:54
  • For the third time. `msg` is a property on LabelBase, with a setter method. **This is exactly the same as your first example**. It doesn't matter what StringProperty is, the assignment is taking place on LabelBase, which is where the property and its setter method are. – Daniel Roseman Nov 23 '18 at 08:58
  • if i do `a = Foo()` and `a = 5` in other class i take type a - int – Shatten Nov 23 '18 at 09:03
  • Yes. That is how assignment works in Python. – Daniel Roseman Nov 23 '18 at 09:06
  • I do not know who voted negative for the answer with the Python 2, it was not me. I'm running in python 3 – Shatten Nov 23 '18 at 09:12
  • You may find this article helpful: [Facts and myths about Python names and values](http://nedbatchelder.com/text/names.html), which was written by SO veteran Ned Batchelder. – PM 2Ring Nov 23 '18 at 09:22
  • I have my doubts about this line `ValueError: LabelBase.msg accept only str`. Can you copy and paste the error exactly as it is rather than from memory? – Silver Nov 23 '18 at 09:27
  • I added a topic – Shatten Nov 23 '18 at 09:33
  • 1
    Okay after reading the source code I understand what they're doing, so what is it exactly that you want? Because what kivy is doing and what you're asking to do (claiming that kivy does it) are completely different. – Silver Nov 23 '18 at 09:44
  • I may not understand correctly what is happening, I want to understand how they do it – Shatten Nov 23 '18 at 09:48
  • You can define properties in a class only. – Stop harming Monica Nov 23 '18 at 10:00

2 Answers2

1

If you want to check the value, you simply add check code to your setter. For example, if you want var to be limited to int or float, you can modify your setter as:

   @var.setter
   def var(self, value):
      if type(value) not in (int, float):
          raise ValueError('%s.%s accept only int or float (got %r)' % (
              self.__class__.__name__,
              'var', value))
      self._var = value

This code is a slight modification of Kivy check code.

John Anderson
  • 35,991
  • 4
  • 13
  • 36
0

I assume you want to do some checks on assignments, like type checking in the kivy case. Your example looks to me very unidiomatic, I'd write it the following way:

class Foo:

    @property
    def var(self):
        return self._var

    @var.setter
    def var(self, value):
        if type(value) == str:
            self._var = value

How does it work? Actually it's a bit complicated, but here is a good ressource. property returns a descriptor which basically means that foo.var = x is translated to foo.var.__set__(x) (which then calls something "equivalent" to Foo.var.setter_function(foo, x)). It just says "instead of storing an assigned value, call this function".

How is the kivy case different? Assume we have the following class:

class Bar(Widget):
    var = StringProperty()

The behavior is very similar to the python code before, but the setter and getter methods are defined by kivy and not here in the class. But if you assign a value to a Bar instance bar.var = x a setter is called bar.var.__set__(x). The setter does not only check for types but also emits events if the value changed.

You can also create properties with getters and setters already provided by implementing a descriptor. You need to implement __set__ and __get__:

class MyStringProperty:

    def __init__(self, default):
        self._default = default

    def __set__(self, instance, value):
        if type(value) == str:
            instance._value = value

    def __get__(self, instance, owner):
        return getattr(instance, "_value", self._default)

class Foo:
    var = MyStringProperty("x")

foo = Foo()
print(foo.var)
foo.var = 3
print(foo.var)
foo.var = "asdf"
print(foo.var)

(The documentation speaks from get, set, delete and set_name, but I think in the common case one can get away with only implementing set and get.)

syntonym
  • 7,134
  • 2
  • 32
  • 45