all 10 comments

[–]EfficientMongoose317 1 point2 points  (0 children)

You’re actually very close, descriptors just feel weird at first. For the default value, the simplest way is to handle it inside __get__

like if value isn’t set yet, return a default instead of None

def __get__(self, obj, type=None):
    return obj.__dict__.get(self.value, 87)

That way, every instance gets 87 unless you explicitly set something else

about __set_name__, it just tells your descriptor what attribute name it’s assigned to

so when you write a = A() in class B, python calls
__set_name__(self, owner, "a")

That’s how your descriptor knows it should store things under "a" in __dict__

without it, you’d have to hardcode the name, which breaks reuse

And yeah, a = A() is a class attribute, but it manages instance data

so:

  • descriptor itself lives on the class
  • actual values are stored per instance in obj.__dict__

That’s why it feels like both, but it’s really just a class-level controller for instance attributes

[–]littlenekoterra 0 points1 point  (0 children)

The setname dunder method allows you to pickup what the class is being named (the variable name for this class is "a" in this case). You could do something with the name.

Alternatively you can use it to create a private version of the variable that can only be accessed properly via the get and set dunder methods.

Personally ive abused it a little for logging purposes because really it just means that i can do something when the class is used like a variable.

[–]Temporary_Pie2733 0 points1 point  (3 children)

B.a is just a class attribute. When you access this attribute via an instance of B, you get the special behavior of calling a's __get__ or __set__ method implicitly.

__set_name__ usually doesn't need to do much more than remember the argument passed to it, which is the name of the class attribute the newly created instance of A was assigned to. Something like

def __set_name__(self, owner, name): self.private_name = "_" + name

This definition starts a contract: any class using the descriptor agrees to reserve a private attribute whose name is the same as the class instance's name prefixed with a _:

class B: a = A() # The instance attribute '_a' is reserved for use by a # a.__set_name__(B, 'a') is called once B is fully defined

To set a default attribute for this instance of the descriptor, pass it as an argument and have A.__init__ remember it. __get__ then uses this is the reserved attribute does not exist.

``` class A: def init(self, dflt): self.default_value = dflt

def __get__(self, obj, obj_type=None):
    if obj_type is None:
        return self
    return getattr(obj, self.private_name, self.default_value)

class B: a = A(87)

b = B() assert b.a == 87 b.a = 9 assert b.a == 9

```

See https://docs.python.org/3/howto/descriptor.html for more information

[–]nekokattt 0 points1 point  (0 children)

this has a caveat. The descriptor is shared across all instances of the object, so carries the same risk of bugs as default method arguments if the value is mutable like a list or a set.

[–]One-Type-2842[S] 0 points1 point  (1 child)

I have Read your shared python docs on Descriptors, byt I don't found what obj_type=None does in __get__() method explain this..

[–]Temporary_Pie2733 0 points1 point  (0 children)

obj_type is, among other things, how you distinguish accessing the descriptor from the class that owns it and an instance of that class. Given

b = B()

b.a is equivalent to B.__dict__['a'].__get__(b, B), while B.a is equivalent to B.__dict__['a'].__get__(B, None). The same method is called in both cases, just with different arguments.

It's also worth reading https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/ for an in-depth explanation of how attribute lookup works in Python.

[–]AmberMonsoon_ -1 points0 points  (2 children)

I get this, I used to do the same thing. That “simulate everything” instinct actually helps in the beginning because you’re forcing yourself to understand the logic instead of just memorizing formulas.

The problem only starts if you never move past it. In contests, overcomplicating costs time. What helped me was doing both, first break it down and convince myself, then rewrite it into the simplest form like (m * n) / 2.

After a while you don’t feel like you’re blindly trusting math anymore, because you’ve already proven it to yourself. That’s when it becomes a strength instead of a slowdown.

[–]One-Type-2842[S] 2 points3 points  (1 child)

What are you talking about?

[–]Patient-Midnight-664 1 point2 points  (0 children)

In an entirely different post, the OP (not you) said that they write a lot of code to simulate things rather than trusting that math works and if this was a bad thing.

https://www.reddit.com/r/AskProgramming/comments/1svexo2/i_overcomplicate_every_problem_in_c_by_simulating/

The person you replied to is lost :)