Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exercise 5.4 Need help with eliminating names from typedproperty #35

Open
constant5 opened this issue Jul 28, 2023 · 7 comments
Open

Exercise 5.4 Need help with eliminating names from typedproperty #35

constant5 opened this issue Jul 28, 2023 · 7 comments

Comments

@constant5
Copy link

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.

@hustmilan
Copy link

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

@sexyprofessor
Copy link

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

@jrmylow
Copy link

jrmylow commented Aug 25, 2023

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

@hustmilan
Copy link

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.

@dabeaz
Copy link
Contributor

dabeaz commented Aug 25, 2023

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.

@tryinghardtolearn
Copy link

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

@mikiab
Copy link

mikiab commented Sep 28, 2024

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants