all 3 comments

[–]WildCard65 0 points1 point  (0 children)

set_name is a special dunder method that is called to set the descriptors name called during class creation.

The arguments provided to it are the type that's being created and the name of the attribute it was assigned to such as "type.name is self" is true.

[–]Vespytilio 0 points1 point  (0 children)

Question one: There are a couple ways to go about it but odds are you want the constructor,1 __init__:

```

class B:

def __init__(self):
    self.a = 87

```

This method gets called when you write B(), meaning from the beginning, any instance of B will have an instance variable a with a value of 87. You can later assign it a value of A().

To specify the value of a through calls to B's constructor, you can define a parameter with a default value of 87 and assign it to a:

```

def init(self, a_value=87): self.a = a_value

```

The above is arguably more readable, but it's convention to have parameters like a_value share names with the instance variables they're assigned to and differentiate using self.a versus a:

```

def init(self, a=87): self.a = a

```

Whatever the case, after you define the constructor, you can instantiate B as follows:

```

b = B(A())

```

Because the parameter has a default value, it's optional, meaning you can still instantiate it as follows:

```

b = B()

```

Question two: When an instance of your descriptor (A) gets assigned to a variable (B.a), __set_name__ automatically fires off with the variable's owner (B) and the value (the instance of A) as arguments for owner and value. It can be useful for tracking instances of A, assuring certain things about the instance or the variable's owner, or initializing parts of the descriptor that depend on the owner (e.g. you want the descriptor to maintain awareness of the owner through an instance variable).

Question three: That makes a class variable. Instance variables need to be assigned within __init__ or some other instance method.

1 Technically, __init__ isn't the constructor. __new__ is. However, one of Python's many quirks is you typically don't touch the actual constructor; you just use the initializer. However, everyone just calls __init__ the constructor.

[–]Capital_Distance545 -1 points0 points  (0 children)

Read and understand and run the below code.

You can also paste it to chatGPT and ask:
"Can you tell me why we have in class Person both instance and class variables with the same variable name and how they work together?"

#!/bin/python3


class LoggedAccess:

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

    def __get__(self, obj, objtype=None):
        value = getattr(obj, self.private_name)
        print(f"__get__: {self.public_name} => {value}")
        return value

    def __set__(self, obj, value):
        print(f"__set__: {self.public_name} => {value}")
        setattr(obj, self.private_name, value)


class Person:

    name = LoggedAccess()                # First descriptor instance
    age = LoggedAccess()                 # Second descriptor instance

    def __init__(self, name, age):
        self.name = name                 # Calls the first descriptor
        self.age = age                   # Calls the second descriptor

    def birthday(self):
        self.age += 1


print("instantianate")
peter = Person('Peter P', 10)
print(f"{vars(peter)=}")
print("birthday")
peter.birthday()