Python Forum
problem descriptors in Python
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
problem descriptors in Python
#1
hi
the below code is in address:
https://docs.python.org/3/howto/descriptor.html
import os

class DirectorySize:

    def __get__(self, obj, objtype=None):
        return len(os.listdir(obj.dirname))

class Directory:

    size = DirectorySize()              # Descriptor instance

    def __init__(self, dirname):
        self.dirname = dirname          # Regular instance attribute

s = Directory('songs')
g = Directory('games')
s.size                              # The songs directory has twenty files

g.size                              # The games directory has three files

os.remove('games/chess')            # Delete a game
g.size                              # File count is automatically updated
please note that you must change songs and games to two folders names in your system.
in the above address, it is quoted that:
Quote:Besides showing how descriptors can run computations, this example also reveals the purpose of the parameters to __get__(). The self parameter is size, an instance of DirectorySize. The obj parameter is either g or s, an instance of Directory. It is the obj parameter that lets the __get__() method learn the target directory. The objtype parameter is the class Directory.

how can I change the __get__ method to show(print at output) what is self and what is obj and what is objtype,namely for s=directory(..), output shows size and s and Directory

I read the first section of the above address, but I did not understand it. can you explain descriptors in Python?
thanks
Reply
#2
Looks like a descriptor is an object that returns a value when referenced. This differs from most objects that return themselves when referenced. The returned value is the result of a computation. Your example could be written to use a method instead of a descriptor.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute

    def size(self):
        return len(os.listdir(self.dirname))


s = Directory(".")
print(s.size())
Notice that size is a method and must be called.

As the article goes on to discuss, a property is a kind of descriptor.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute

    @property
    def size(self):
        return len(os.listdir(self.dirname))


s = Directory(".")
print(s.size)
Notice that the property size is referenced like an instance variable even though it executes a method when referenced. The main difference between a property and a descriptor is that a property is an attribute of a class, but a descriptor is a standalone object.

A property is different than an instance variable in that it executes code to get the value. You could not do this.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute
        self.size = len(os.listdir(self.dirname))


s = Directory(".")
print(s.size)
os.remove('somefile.txt')
print(s.size)
In this example the directory size is not recomputed, so size remains the initial size of the directory and doesn't change to show the new size after the file is removed.
Quote:how can I change the __get__ method to show(print at output) what is self and what is obj and what is objtype,namely for s=directory(..), output shows size and s and Directory
You could add a print statment to the method.
class DirectorySize:
    def __get__(self, obj, objtype=None):
        print(self, obj, objtype)
        return len(os.listdir(obj.dirname))
The print statement will show you that self is a DirectorySize object and obj is a Directory object (s or g in your example).

Putting print statements in methods is usually a bad idea, unless the method is only used for the side effect of printing to output
akbarza likes this post
Reply
#3
(Dec-26-2023, 11:13 AM)akbarza Wrote: can you explain descriptors in Python?
A descriptor is an object that has a __get__() method and potentially also a __set__() and a __del__() method. This descriptor can be used as a member in another class to emulate a dynamic attribute.

In the below example, we create a MyDescriptor instance as the member 'spam' in the dictionary of class Thing. When we call vars(Thing)['spam'], we get the MyDescriptor instance that is stored in class Thing.
class MyDescriptor:
    def __get__(self, obj, objtype=None):
        return (self, obj, objtype)


class Thing:
    spam = MyDescriptor()


thing = Thing()

print(thing.spam)
print(Thing.spam)
print(vars(Thing)['spam'])
Output:
(<__main__.MyDescriptor object at 0x7f4e7f157fd0>, <__main__.Thing object at 0x7f4e7f157eb0>, <class '__main__.Thing'>) (<__main__.MyDescriptor object at 0x7f4e7f157fd0>, None, <class '__main__.Thing'>) <__main__.MyDescriptor object at 0x7f4e7f157fd0>
The magic of the __get__() method is that the expression Thing.spam evaluates to the return value of the descriptor's __get__() method called with 3 arguments: the descriptor itself, None and the Thing class.

When thing is an instance of Thing, the expression thing.spam evaluates to the return value of the descriptor's __get__() method called with 3 arguments: the descriptor itself, the Thing instance thing and the Thing class.

In the above example, the __get__() method returns its tuple of arguments, but it could return anything.
akbarza likes this post
« We can solve any problem by introducing an extra level of indirection »
Reply
#4
(Dec-26-2023, 03:43 PM)deanhystad Wrote: Looks like a descriptor is an object that returns a value when referenced. This differs from most objects that return themselves when referenced. The returned value is the result of a computation. Your example could be written to use a method instead of a descriptor.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute

    def size(self):
        return len(os.listdir(self.dirname))


s = Directory(".")
print(s.size())
Notice that size is a method and must be called.

As the article goes on to discuss, a property is a kind of descriptor.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute

    @property
    def size(self):
        return len(os.listdir(self.dirname))


s = Directory(".")
print(s.size)
Notice that the property size is referenced like an instance variable even though it executes a method when referenced. The main difference between a property and a descriptor is that a property is an attribute of a class, but a descriptor is a standalone object.

A property is different than an instance variable in that it executes code to get the value. You could not do this.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute
        self.size = len(os.listdir(self.dirname))


s = Directory(".")
print(s.size)
os.remove('somefile.txt')
print(s.size)
In this example the directory size is not recomputed, so size remains the initial size of the directory and doesn't change to show the new size after the file is removed.
Quote:how can I change the __get__ method to show(print at output) what is self and what is obj and what is objtype,namely for s=directory(..), output shows size and s and Directory
You could add a print statment to the method.
class DirectorySize:
    def __get__(self, obj, objtype=None):
        print(self, obj, objtype)
        return len(os.listdir(obj.dirname))
The print statement will show you that self is a DirectorySize object and obj is a Directory object (s or g in your example).

Putting print statements in methods is usually a bad idea, unless the method is only used for the side effect of printing to output

hi
I wrote the first code of you in idle and then I wrote the below command but I took error:
d=Directory("C:\Users\akbar\Desktop")
Error:
SyntaxError: incomplete input
d=Directory('C:\Users\akbar\Desktop\')
Error:
SyntaxError: incomplete input
why?
Reply
#5
(Dec-26-2023, 03:43 PM)deanhystad Wrote: Looks like a descriptor is an object that returns a value when referenced. This differs from most objects that return themselves when referenced. The returned value is the result of a computation. Your example could be written to use a method instead of a descriptor.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute

    def size(self):
        return len(os.listdir(self.dirname))


s = Directory(".")
print(s.size())
Notice that size is a method and must be called.

As the article goes on to discuss, a property is a kind of descriptor.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute

    @property
    def size(self):
        return len(os.listdir(self.dirname))


s = Directory(".")
print(s.size)
Notice that the property size is referenced like an instance variable even though it executes a method when referenced. The main difference between a property and a descriptor is that a property is an attribute of a class, but a descriptor is a standalone object.

A property is different than an instance variable in that it executes code to get the value. You could not do this.
import os


class Directory:
    def __init__(self, dirname):
        self.dirname = dirname  # Regular instance attribute
        self.size = len(os.listdir(self.dirname))


s = Directory(".")
print(s.size)
os.remove('somefile.txt')
print(s.size)
In this example the directory size is not recomputed, so size remains the initial size of the directory and doesn't change to show the new size after the file is removed.
Quote:how can I change the __get__ method to show(print at output) what is self and what is obj and what is objtype,namely for s=directory(..), output shows size and s and Directory
You could add a print statment to the method.
class DirectorySize:
    def __get__(self, obj, objtype=None):
        print(self, obj, objtype)
        return len(os.listdir(obj.dirname))
The print statement will show you that self is a DirectorySize object and obj is a Directory object (s or g in your example).

Putting print statements in methods is usually a bad idea, unless the method is only used for the side effect of printing to output

hi
i changed DirectorySize class( i added the line print(self.obj,objtype)). the output was:
<__main__.DirectorySize object at 0x000001E97B187A10> <__main__.Directory object at 0x000001E97B187FD0> <class '__main__.Directory'>
Reply
#6
(Dec-26-2023, 04:15 PM)Gribouillis Wrote:
(Dec-26-2023, 11:13 AM)akbarza Wrote: can you explain descriptors in Python?
A descriptor is an object that has a __get__() method and potentially also a __set__() and a __del__() method. This descriptor can be used as a member in another class to emulate a dynamic attribute.

In the below example, we create a MyDescriptor instance as the member 'spam' in the dictionary of class Thing. When we call vars(Thing)['spam'], we get the MyDescriptor instance that is stored in class Thing.
class MyDescriptor:
    def __get__(self, obj, objtype=None):
        return (self, obj, objtype)


class Thing:
    spam = MyDescriptor()


thing = Thing()

print(thing.spam)
print(Thing.spam)
print(vars(Thing)['spam'])
Output:
(<__main__.MyDescriptor object at 0x7f4e7f157fd0>, <__main__.Thing object at 0x7f4e7f157eb0>, <class '__main__.Thing'>) (<__main__.MyDescriptor object at 0x7f4e7f157fd0>, None, <class '__main__.Thing'>) <__main__.MyDescriptor object at 0x7f4e7f157fd0>
The magic of the __get__() method is that the expression Thing.spam evaluates to the return value of the descriptor's __get__() method called with 3 arguments: the descriptor itself, None and the Thing class.

When thing is an instance of Thing, the expression thing.spam evaluates to the return value of the descriptor's __get__() method called with 3 arguments: the descriptor itself, the Thing instance thing and the Thing class.

In the above example, the __get__() method returns its tuple of arguments, but it could return anything.
hi
can I ask you what is the __main__ that appears in all output in running above code?
Reply
#7
(Dec-27-2023, 07:20 AM)akbarza Wrote: can I ask you what is the __main__ that appears in all output in running above code?
"__main__" is the name of the main module of the running Python program. When a class Spam is defined in a module named eggs, Python refers to this class as class eggs.Spam. It is called a "qualified name" of the class. Thus __main__.MyDescriptor means that it refers to the class named MyDescriptor in the module named __main__.
akbarza likes this post
« We can solve any problem by introducing an extra level of indirection »
Reply
#8
why d=Directory('C:\Users\akbar\Desktop\') causes error?
Reply
#9
Quote:i changed DirectorySize class( i added the line print(self.obj,objtype)). the output was:
<__main__.DirectorySize object at 0x000001E97B187A10> <__main__.Directory object at 0x000001E97B187FD0> <class '__main__.Directory'>
That is what I would expect. self is a DirectorySize object and obj is a Directory object. Do you have a question related to this?

Quote:why d=Directory('C:\Users\akbar\Desktop\') causes error?
In Python you cannot end a string with a single backslash. There is also a problem that "\U" is the start of an escape sequence. Python expects \U to be followed by numbers. This is the error I see.
Error:
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
Bad error reporting is yet another reason for not using IDLE.
You could try: d=Directory("C:\\Users\akbar\Desktop"), but if you don't know all the escape sequences it is probably better to use:
d=Directory("C:\\Users\\akbar\\Desktop")
Or better yet:
d=Directory("C:/Users/akbar/Desktop")
Stop using "\" as a separator in a file path. Start using "/".
akbarza likes this post
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  does open() still take file descriptors in py3 Skaperen 2 3,361 Jan-25-2017, 02:30 AM
Last Post: Skaperen
  file descriptors Skaperen 7 6,574 Jan-15-2017, 09:18 AM
Last Post: Skaperen

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020