🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.lang.python
Article #26988

Re: dyskryptory

#26988
From: ak3031884
Date: Sun, 29 Nov 2009 20:01
256 lines
9878 bytes
Wielkie dzięki super opis.



On 29 Lis, 12:52, Rob Wolfe <r...@smsnet.pl> wrote:
> ak3031884 <ak3031...@gmail.com> writes:
> > Tak ten przykład podoba mi sie i pokazuje słuszność stosowania __set__
> > i __get__, aczkolwiek w  "starym stylu klas" można to też ładnie
> > napisać.
>
> > Powiedzcie mi jeszcze jaka jest różnica między funkcjami
> > object.__getattr__(self, name) a   def __get__(self, obj, cls):
> > object.__setattr__(self, name, value) a def __set__(self, obj, value):
>
> > czytająchttp://docs.python.org/reference/datamodel.htmljakoś nie
> > bardzo pojąłem wielkość różnicy tych funkcji.
>
> Jeśli masz ambicję zrozumienia deskryptorów, to musisz przeczytać opis
> (najlepszy w sieci, napisany przez twórcę tej koncepcji),
> do którego link już Ci został podany:http://users.rcn.com/python/download/Descriptor.htm
>
> Gdy tam zajrzysz, to znajdziesz kluczową definicję dla zrozumienia
> tego mechanizmu:
>
> def __getattribute__(self, key):
>     "Emulate type_getattro() in Objects/typeobject.c"
>     v = object.__getattribute__(self, key)
>     if hasattr(v, '__get__'):
>        return v.__get__(None, self)
>     return v
>
> Kluczowa zasada to kolejność wywołań:
> 1. __getattribute__ jest wołana zawsze jako pierwsza
> 2. __get__ jest wołana tylko dla atrybutów, które mają
>    zdefiniowaną taką metodę, czyli są deskryptorami
> 3. __getattr__ jest wołana zawsze jako ostatnia
>   i tylko wtedy, gdy powyższy mechanizm nie był w stanie
>   odnaleźć składowej
>
> Czyli gdy mamy zdefiniowaną tylko `__getattribute__`,
> to wołana jest zawsze:
>
> class A(object):
>     def __init__(self):
>         self.x = 99
>
>     def __getattribute__(self, name):
>         print '__getattribute__: %r' % name
>         v = object.__getattribute__(self, name)
>         if hasattr(v, '__get__'):
>             return v.__get__(None, self)
>         return v
>
> >>> a = A()
> >>> a.x
>
> __getattribute__: 'x'
> 99>>> a.y
>
> __getattribute__: 'y'
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "/tmp/python-4766uIx.py", line 7, in __getattribute__
> AttributeError: 'A' object has no attribute 'y'
>
> Gdy mamy zdefiniowaną `__getattr__`, to wołana jest tylko
> przy odwołaniu do *niezdefiniowanej* składowej:
>
> class B(object):
>     def __init__(self):
>         self.x = 99
>
>     def __getattr__(self, name):
>         print '__getattr__: %r' % name
>         if name == 'y':
>             return self.x * 10  # odwolanie do self.x nie powoduje zapetlenia
>         raise AttributeError('%r object has no attribute %r'
>                              % (self.__class__.__name__, name))
>
> >>> b = B()
> >>> b.x
> 99
> >>> b.y
>
> __getattr__: 'y'
> 990>>> b.z
>
> __getattr__: 'z'
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "/tmp/python-4766gSA.py", line 10, in __getattr__
> AttributeError: 'B' object has no attribute 'z'
>
> Gdy mamy zdefiniowane zarówno `__getattribute__` jak i `__getattr__`,
> to wywoływane są w z góry przyjętej kolejności, czyli na początku
> `__getattribute__`, a na końcu `__getattr__` (gdy jest odwołanie
> do *niezdefiniowanej* składowej):
>
> class C(object):
>     def __init__(self):
>         self.x = 99
>
>     def __getattribute__(self, name):
>         print '__getattribute__: %r' % name
>         v = object.__getattribute__(self, name)
>         if hasattr(v, '__get__'):
>             return v.__get__(None, self)
>         return v
>
>     def __getattr__(self, name):
>         print '__getattr__: %r' % name
>         if name == 'y':
>             return self.x * 10  # odwolanie do self.x nie powoduje zapetlenia
>         raise AttributeError('%r object has no attribute %r'
>                              % (self.__class__.__name__, name))
>
> >>> c = C()
> >>> c.x
>
> __getattribute__: 'x'
> 99>>> c.y
>
> __getattribute__: 'y'
> __getattr__: 'y'
> __getattribute__: 'x'
> 990>>> c.z
>
> __getattribute__: 'z'
> __getattr__: 'z'
> __getattribute__: '__class__'
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "/tmp/python-4766tcG.py", line 17, in __getattr__
> AttributeError: 'C' object has no attribute 'z'
>
> I teraz, gdy do tej klasy dorzucimy definicję `__get__`, to co się stanie?
> Mogą się dziać dziwne rzeczy, gdyż `__get__` w takim przypadku nie ma
> najmniejszego sensu:
>
> class D(object):
>     def __init__(self):
>         self.x = 99
>
>     def __getattribute__(self, name):
>         print '__getattribute__: %r' % name
>         v = object.__getattribute__(self, name)
>         if hasattr(v, '__get__'):
>             return v.__get__(None, self)
>         return v
>
>     def __get__(self, instance, owner):
>         print '__get__: %r' % owner
>         return self.x
>
>     def __getattr__(self, name):
>         print '__getattr__: %r' % name
>         if name == 'y':
>             return self.x * 10  # odwolanie do self.x nie powoduje zapetlenia
>         raise AttributeError('%r object has no attribute %r'
>                              % (self.__class__.__name__, name))
>
> >>> d = D()
> >>> d.x
>
> __getattribute__: 'x'
> 99>>> d.y
>
> __getattribute__: 'y'
> __getattr__: 'y'
> __getattribute__: 'x'
> 990>>> d.z
>
> __getattribute__: 'z'
> __getattr__: 'z'
> __getattribute__: '__class__'
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "/tmp/python-4766U7Y.py", line 21, in __getattr__
>   File "/tmp/python-4766U7Y.py", line 9, in __getattribute__
> TypeError: unbound method __get__() must be called with D instance as first argument (got NoneType instance instead)
>
> Zgodnie z dokumentacją [1]_ ma sens tylko dla:
> """
> The following methods (przyp. tlum. __get__, __set__, __delete__) only
> apply when an instance of the class containing the method
> (a so-called descriptor class) appears in the class dictionary of
> another new-style class, known as the owner class.
> """
>
> czyli po naszemu:
>
> """
> Metody __get__, __set__ i __delete__ mają zastosowanie wtedy i tylko wtedy,
> gdy instancja klasy zawierająca te metody (tzw. deskryptor) stanowi składową
> innej klasy nowego stylu.
> """
>
> W związku z tym deskryptor ma sens w takim przypadku jak ten, gdzie mamy
> oddzielną klasę definiującą składową i jakąś klasę zawierjącą
> tę składową:
>
> class E(object):
>     def __init__(self, val):
>         self.val = val
>
>     def __get__(self, instance, owner):
>         print '__get__: %r' % owner.__class__.__name__
>         return self.val
>
> class F(object):
>     def __init__(self):
>         self.x = E(99)
>
>     def __getattribute__(self, name):
>         print '__getattribute__: %r' % name
>         v = object.__getattribute__(self, name)
>         if hasattr(v, '__get__'):
>             return v.__get__(None, self)
>         return v
>
>     def __getattr__(self, name):
>         print '__getattr__: %r' % name
>         if name == 'y':
>             return self.x * 10  # odwolanie do self.x nie powoduje zapetlenia
>         raise AttributeError('%r object has no attribute %r'
>                              % (self.__class__.__name__, name))
>
> >>> f = F()
> >>> f.x
>
> __getattribute__: 'x'
> __getattribute__: '__class__'
> __get__: 'F'
> 99>>> f.y
>
> __getattribute__: 'y'
> __getattr__: 'y'
> __getattribute__: 'x'
> __getattribute__: '__class__'
> __get__: 'F'
> 990>>> f.z
>
> __getattribute__: 'z'
> __getattr__: 'z'
> __getattribute__: '__class__'
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "/tmp/python-4766uPl.py", line 26, in __getattr__
> AttributeError: 'F' object has no attribute 'z'
>
> A teraz poćwicz ten temat tak z tydzień w różnych wariantach
> i jest pewna szansa, że coś się zacznie klarować. ;)
>
> .. [1]http://docs.python.org/reference/datamodel.html?highlight=__getattr__...
>
> RW
Technical Details
Message-ID: <7670a2c9-059b-454d-b06f-65171b8474ca@j24g2000yqa.googlegroups.com>
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!wsisiz.edu.pl!plix.pl!newsfeed1.plix.pl!news-out1.kabelfoon.nl!newsfeed.kabelfoon.nl!xindi.nntp.kabelfoon.nl!feeder.news-service.com!postnews.google.com!j24g2000yqa.googlegroups.com!not-for-mail
References: <slrnhh0li5.ljo.beorn@pino.cyberdeck.pl> <slrnhh0pgf.llt.beorn@pino.cyberdeck.pl> <her5pc$22d$1@inews.gazeta.pl> <62601031-a50a-49d3-b704-4969ea607794@k4g2000yqb.googlegroups.com> <87d431o8dk.fsf@smsnet.pl>

Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!wsisiz.edu.pl!plix.pl!newsfeed1.plix.pl!news-out1.kabelfoon.nl!newsfeed.kabelfoon.nl!xindi.nntp.kabelfoon.nl!feeder.news-service.com!postnews.google.com!j24g2000yqa.googlegroups.com!not-for-mail
From: ak3031884 <ak3031884@gmail.com>
Newsgroups: pl.comp.lang.python
Subject: Re: dyskryptory
Date: Sun, 29 Nov 2009 12:01:55 -0800 (PST)
Organization: http://groups.google.com
Lines: 278
Message-ID: <7670a2c9-059b-454d-b06f-65171b8474ca@j24g2000yqa.googlegroups.com>
References: <slrnhh0li5.ljo.beorn@pino.cyberdeck.pl> <slrnhh0pgf.llt.beorn@pino.cyberdeck.pl>  <her5pc$22d$1@inews.gazeta.pl> <62601031-a50a-49d3-b704-4969ea607794@k4g2000yqb.googlegroups.com>  <87d431o8dk.fsf@smsnet.pl>
NNTP-Posting-Host: 83.6.113.117
Mime-Version: 1.0
X-Trace: posting.google.com 1259524915 958 127.0.0.1 (29 Nov 2009 20:01:55 GMT)
X-Complaints-To: groups-abuse@google.com
NNTP-Posting-Date: Sun, 29 Nov 2009 20:01:55 +0000 (UTC)
Complaints-To: groups-abuse@google.com
Injection-Info: j24g2000yqa.googlegroups.com; posting-host=83.6.113.117;  posting-account=2S2y4QoAAACzknOmZD98y2Ho84xcl2a1
User-Agent: G2/1.0
X-HTTP-UserAgent: Opera/9.64 (Windows NT 5.1; U; pl) Presto/2.1.1,gzip(gfe),gzip(gfe)
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit