python-mastery icon indicating copy to clipboard operation
python-mastery copied to clipboard

Exercise 5.4 Need help with eliminating names from typedproperty

Open constant5 opened this issue 2 years ago • 7 comments

In the last challenge of this exercise, we are asked to eliminate the need for providing names to typedproperty. I can't get this to work with the closure template provided. Can I get some more hints or just the solution, as I have been banging my head against this problem for a couple of hours now? I realize that __set_name__ is called under the hood when assigning properties to the class, and this was pretty clear in the valiidate.py exercise. I'm unsure how this works when extending the class with these typedproperty methods. Thanks for the help.

constant5 avatar Jul 28 '23 16:07 constant5

I'm also curious to see how to bind set_name function with property object. Any hint or solution will be appreciated

hustmilan avatar Aug 11 '23 04:08 hustmilan

how to bind set_name function with property object.

class MyClass: def init(self): self._name = ""

def get_name(self):
    return self._name

def set_name(self, value):
    self._name = value

name = property(get_name, set_name)

obj = MyClass() obj.name = "blah" print(obj.name) # Output: blah

sexyprofessor avatar Aug 21 '23 14:08 sexyprofessor

Is discussing solutions ok in this thread?

Spoiler warning for my solution

Unsure if this is the intended solution or I've missed something which may cause a bug later on. I tried to override the __set_name__ method of the property. Unfortunately, it's read-only.

The next attempt consisted of subclassing from property:

class special_property(property):
    def __set_name__(self, cls, name):
        self.public_name = name
        self.private_name = '_' + name
        super().__set_name__(cls, name)

The instance, value now knows its own public and private names, and is accessed via:

def typedproperty(expected_type):
    @special_property
    def value(self):
        return getattr(self, value.private_name)
    
    @value.setter
    def value(self, val):
        if not isinstance(val, expected_type):
            raise TypeError(f'Expected {expected_type}')
        setattr(self, value.private_name, val)
    
    return value

jrmylow avatar Aug 25 '23 12:08 jrmylow

Thanks, jrmylow.

After commenting out this line "super().__set_name__(cls, name)" in "__set_name__" method of special_property class, your solution works well now. Otherwise, it will throw some error.

hustmilan avatar Aug 25 '23 13:08 hustmilan

To answer an earlier question, I'm okay with solution discussion here. I never provided a solution to this (or any hints really). However, the only mechanism that Python provides to learn the name is __set_name__(). So, whatever the solution might be, it has to involve that in some way.

One thing that is a little unsettling in this exercise is the idea that you could dynamically create a property inside a function and return it back as an object like this. Python lets you create anything you want inside a function and return it back. This has some application later when the course gets into metaprogramming.

dabeaz avatar Aug 25 '23 14:08 dabeaz

Hint: use set_name inside the typedproperty function so that it can access the name of the attribute that it's assigned to.

Spoiler alert
def typedproperty(expected_type):
    def __set_name__(attr_name):
        private_name = "_" + attr_name
        @property
        def value(self):
            return getattr(self, private_name)

        @value.setter
        def value(self, value):
            if not isinstance(value, expected_type):
                raise TypeError(F"Expected {expected_type}")
            setattr(self, private_name, value)

        return value

tryinghardtolearn avatar Sep 14 '24 03:09 tryinghardtolearn

This is my solution:

def typedproperty(expected_type):
    private_name = None

    class NamedProperty(property):
        def __set_name__(self, cls, name):
            nonlocal private_name 
            private_name = '_' + name

    def _get(self):
        return getattr(self, private_name)
    
    def _set(self, val):
        if not isinstance(val, expected_type):
            raise TypeError(f"Expected {expected_type}")
        setattr(self, private_name, val)
       
    return NamedProperty(_get, _set)

mikiab avatar Sep 28 '24 16:09 mikiab