[Tutor] Question on implmenting __getitem__ on custom classes
Hi, I wrote below 2 classes to explore how __getitem__(self,k) works in conjuection with list subscriptions. Both code works. Now my questions which way the community encourages more in Python: if isinstance(key, slice): or if type(key) == slice: ? How should I implement this if I follow duck typing, because none of the code currently I wrote using duck typing techiniqe IMO. class MyCustomList: def __init__(self, list = []): self.list = list def __getitem__(self, key): if isinstance(key, slice): return self.list[key] else: return self.list[key] class MyCustomListV1: def __init__(self, list = []): self.list = list def __getitem__(self, key): if type(key) == slice: return self.list[key] else: return self.list[key] if __name__ == '__main__': list = MyCustomList(list=[1, 2, 3, 4, 5, 6]) print(list[1:3]) print(list[3]) print("===\n") list = MyCustomListV1(list=[1, 2, 3, 4, 5, 6]) print(list[1:3]) print(list[3]) If run it, I get the output: [2, 3] 4 === [2, 3] 4 -- Thanks, Arup Rakshit ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Question on implmenting __getitem__ on custom classes
On 23/04/2019 07:16, Arup Rakshit wrote: > which way the community encourages more in Python: if isinstance(key, > slice): or if type(key) == slice: ? I think isinstance is usually preferred although I confess that I usually forget and use type()... But isinstance covers you for subclasses too. > class MyCustomList: > def __init__(self, list = []): > self.list = list > > def __getitem__(self, key): > if isinstance(key, slice): > return self.list[key] > else: > return self.list[key] The if/else test is meaningless since you return self.list[key] in both cases. You would be better off just calling it directly (and maybe wrapping that in a try block?) def __getitem__(self, key): try: return self.list[key] except : Assuming you can think of something useful to put in the except clause. Otherwise just let Python raise the exception and leave the user of the class to worry about what to do. -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Question on implmenting __getitem__ on custom classes
On Tue, Apr 23, 2019 at 11:46:58AM +0530, Arup Rakshit wrote: > Hi, > > I wrote below 2 classes to explore how __getitem__(self,k) works in > conjuection with list subscriptions. Both code works. Now my questions > which way the community encourages more in Python: if isinstance(key, > slice): or if type(key) == slice: ? In general, we should normally use `isinstance`, because it works with subclasses. But `slice` can't be subclassed: py> class S(slice): ... pass ... Traceback (most recent call last): File "", line 1, in TypeError: type 'slice' is not an acceptable base type so there is no advantage to using `isinstance`. (There is no disadvantage either.) I would use `type(key) is slice` to guarantee[1] that the key is certainly a slice. Why use `is` instead of `==`? The `is` operator will be a tiny bit faster than `==`, but more importantly, you could have a class designed to pretend to be a slice. It isn't easy to do (you would have to write a metaclass, which makes it an advanced technique) but by using `is` we can eliminate even that slim chance. > How should I implement this if I > follow duck typing, because none of the code currently I wrote using > duck typing techiniqe IMO. Why bother? Duck-typing is good for *data* values, but a slice is not a data value, it is a way of specifying a range of indexes. > class MyCustomList: > def __init__(self, list = []): > self.list = list Watch out here, you have a mutable default value, that probably doesn't work the way you expect. The default value is created ONCE, and then shared, so if you do this: a = MyCustomList() # Use the default list. b = MyCustomList() # Shares the same default list! a.append(1) print(b.list) # prints [1] You probably want: def __init__(self, list=None): if list is None: list = [] self.list = list > def __getitem__(self, key): > if isinstance(key, slice): > return self.list[key] > else: > return self.list[key] The "isinstance" check is useless, because you do precisely the same thing in both branches. def __getitem__(self, key): return self.list[key] will do exactly the same, and more efficiently. [1] Not actually a guarantee. -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Question on implmenting __getitem__ on custom classes
On 23/04/19 3:40 PM, Steven D'Aprano wrote: Watch out here, you have a mutable default value, that probably doesn't work the way you expect. The default value is created ONCE, and then shared, so if you do this: a = MyCustomList() # Use the default list. b = MyCustomList() # Shares the same default list! a.append(1) print(b.list) # prints [1] You probably want: def __init__(self, list=None): if list is None: list = [] self.list = list That is really a new thing to me. I didn't know. Why list=None in the parameter list is different than in the body of __init__ method? Can you elaborate this? -- Thanks, Arup Rakshit ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] What protocol to follow when need to pick either one from __getattr__ and __getattribute__ ?
I read today 2 methods regarding the customizing the attribute access:__getattr__ and __getattribute__ from https://docs.python.org/3/reference/datamodel.html#special-method-names. What I understood about them is that __getattr__ is called when the requested attribute is not found, and an AttributeError is raised. But later is called everytime unconditionally. I wrote a simple 2 input calculator program, where only 2 operations are permitted Addition and Subtraction. Anything else will cause an not permitted error. class OperationNotPermitted(AttributeError): pass class Calc: def __init__(self, x, y): self.x = x self.y = y def __getattr__(self, name): if name == "sum": return self.x + self.y elif name == 'minus': return self.x - self.y else: raise OperationNotPermitted("operation {} is not permitted".format(name)) And here is a run down: from customize_attr_access import * cal = Calc(12, 10) cal.sum 22 cal.minus 2 cal.mul Traceback (most recent call last): Python Shell, prompt 5, line 1 # Used internally for debug sandbox under external interpreter File "/Users/aruprakshit/python_playground/customize_attr_access.py", line 15, in __getattr__ raise OperationNotPermitted("operation {} is not permitted".format(name)) customize_attr_access.OperationNotPermitted: operation mul is not permitted If I replace __getattr__ with __getattribute__ I found the program works exactly same. Now my questions is in real world when you have to pick between these 2 pair of special method which protocols a Python dev checks to pick either of the one? Is there any such thing, or either one is fine. Can anyone elaborate this to educate me please? doc said: > This method should either return the (computed) attribute value or raise an AttributeError exception. Another question: My question is that: Can I raise a domain error like OperationNotPermitted when raising instead of AttributeError ? -- Thanks, Arup Rakshit ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Question on implmenting __getitem__ on custom classes
On 4/23/19 8:57 AM, Arup Rakshit wrote: > On 23/04/19 3:40 PM, Steven D'Aprano wrote: >> Watch out here, you have a mutable default value, that probably doesn't >> work the way you expect. The default value is created ONCE, and then >> shared, so if you do this: >> >> a = MyCustomList() # Use the default list. >> b = MyCustomList() # Shares the same default list! >> a.append(1) >> print(b.list) >> # prints [1] >> >> You probably want: >> >> def __init__(self, list=None): >> if list is None: >> list = [] >> self.list = list > > That is really a new thing to me. I didn't know. Why list=None in the > parameter list is different than in the body of __init__ method? Can you > elaborate this? > It arises because a function definition is an executable statement, run right then. So a default value in the parameter list is created right then, and then used as needed, and if that default is a mutable (list, dictionary, etc) you get surprises. When an empty list is created in the body, it happens each time the function object is called (it's a method, but it's still just a function object). You can write some experiments to show yourself this in action, it usually makes it sink in more than someone telling you. And don't worry, this is one of the most famous of all Python beginner traps, we've all fallen in it. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Question on implmenting __getitem__ on custom classes
On Tue, Apr 23, 2019 at 08:27:15PM +0530, Arup Rakshit wrote: > >You probably want: > > > > def __init__(self, list=None): > > if list is None: > > list = [] > > self.list = list > > That is really a new thing to me. I didn't know. Why list=None in the > parameter list is different than in the body of __init__ method? Can you > elaborate this? Try running this code and see what happens: def make_default(): print("creating a new list") return [] def function(arg=make_default()): arg.append(1) return arg Now call: function() function() function() and see if you can work out what is happening. Hint: how many new lists are created? when are they created? -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] What protocol to follow when need to pick either one from __getattr__ and __getattribute__ ?
On Tue, Apr 23, 2019 at 08:22:56PM +0530, Arup Rakshit wrote: > I read today 2 methods regarding the customizing the attribute > access:__getattr__ and __getattribute__ from > https://docs.python.org/3/reference/datamodel.html#special-method-names. > What I understood about them is that __getattr__ is called when the > requested attribute is not found, and an AttributeError is raised. But > later is called everytime unconditionally. When you overload __getattribute__, your class will be slow because **every** attribute lookup has to go through your method, instead of only the lookups which fail. And unless you are very careful, you will break things: py> class X(object): ... def __init__(self, x): ... self.x = x ... def __getattribute__(self, name): ... if name is 'x': return self.x ... py> obj = X(999) py> obj.x Traceback (most recent call last): File "", line 1, in File "", line 5, in __getattribute__ [ previous line repeats 332 times ] RecursionError: maximum recursion depth exceeded while calling a Python object Normally, overriding __getattribute__ is considered a very advanced and unusual thing to do. -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] What protocol to follow when need to pick either one from __getattr__ and __getattribute__ ?
Arup Rakshit wrote: > I read today 2 methods regarding the customizing the attribute > access:__getattr__ and __getattribute__ from > https://docs.python.org/3/reference/datamodel.html#special-method-names. > What I understood about them is that __getattr__ is called when the > requested attribute is not found, and an AttributeError is raised. But > later is called everytime unconditionally. I wrote a simple 2 input > calculator program, where only 2 operations are permitted Addition and > Subtraction. Anything else will cause an not permitted error. > > class OperationNotPermitted(AttributeError): > pass > > class Calc: > def __init__(self, x, y): > self.x = x > self.y = y > > def __getattr__(self, name): > if name == "sum": > return self.x + self.y > elif name == 'minus': > return self.x - self.y > else: > raise OperationNotPermitted("operation {} is not > permitted".format(name)) > > And here is a run down: > > from customize_attr_access import * > cal = Calc(12, 10) > cal.sum > 22 > cal.minus > 2 > cal.mul > Traceback (most recent call last): > Python Shell, prompt 5, line 1 > # Used internally for debug sandbox under external interpreter > File "/Users/aruprakshit/python_playground/customize_attr_access.py", > line 15, in __getattr__ > raise OperationNotPermitted("operation {} is not > permitted".format(name)) > customize_attr_access.OperationNotPermitted: operation mul is not > permitted > > If I replace __getattr__ with __getattribute__ I found the program works > exactly same. No, it doesn't, as __getattribute__ is called for x, and y, too. def __getattribute__(self, name): if name == "sum": return self.x + self.y elif name == 'minus': return self.x - self.y else: raise OperationNotPermitted("operation {} is not permitted".format(name)) Accessing cal.sum will therefore trigger a __getattribute__("x") call which in turn will raise an OperationNotPermitted("operation x ...") exception. > Now my questions is in real world when you have to pick > between these 2 pair of special method which protocols a Python dev > checks to pick either of the one? Is there any such thing, or either one > is fine. Can anyone elaborate this to educate me please? __getattribute__() is rarely needed, __getattr__() is useful when the list of calculated attributes is open-ended and uniform (think proxy). When there is a finite number of calculated attributes the best way to implement them is usually a property: class Calc: def __init__(self, x, y): self.x = x self.y = y @property def sum(self): return self.x + self.y @property def difference(self): return self.x - self.y > > doc said: > > > This method should either return the (computed) attribute value or > raise an AttributeError exception. > > Another question: > > My question is that: Can I raise a domain error like > OperationNotPermitted when raising instead of AttributeError ? You just did ;) I don't think it's a good idea, though. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] What protocol to follow when need to pick either one from __getattr__ and __getattribute__ ?
On 23/04/2019 15:52, Arup Rakshit wrote: > If I replace __getattr__ with __getattribute__ I found the program works > exactly same. Now my questions is in real world when you have to pick > between these 2 pair of special method which protocols a Python dev > checks to pick either of the one? Is there any such thing, or either one > is fine. Can anyone elaborate this to educate me please? There are some recomendations but I'll leave it to someone better qualified than me to respond. > Another question: > > My question is that: Can I raise a domain error like > OperationNotPermitted when raising instead of AttributeError ? You can raise any kind of error you like however, in general, using the standard error types is better because if your class gets using in another program that already handles the standard error type then your class will get handled too. But, if you raise a non standard error, it may slip through the net. (You can mitigate this by subclassing from the standard error that would normally be raised since catch will catch all subclasses as swell as the stated exception type.) HTH -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Question on implmenting __getitem__ on custom classes
On 23/04/19 10:08 PM, Steven D'Aprano wrote: On Tue, Apr 23, 2019 at 08:27:15PM +0530, Arup Rakshit wrote: You probably want: def __init__(self, list=None): if list is None: list = [] self.list = list That is really a new thing to me. I didn't know. Why list=None in the parameter list is different than in the body of __init__ method? Can you elaborate this? Try running this code and see what happens: def make_default(): print("creating a new list") return [] def function(arg=make_default()): arg.append(1) return arg Now call: function() function() function() and see if you can work out what is happening. Hint: how many new lists are created? when are they created? Hello, You are right. I didn't think on it, as it feels to me very natural as per the experiences from other language like Ruby, JS. It works differently there. A similar Ruby code gives different output than Python does. def make_default() print("creating a new list") return [] end def function(arg=make_default()) arg.push(1) return arg end # Now call: p(function()) p(function()) p(function()) And on run: ruby -v sample.rb ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16] creating a new list[1] creating a new list[1] creating a new list[1] But python gives to me: creating a new list [1] [1, 1] [1, 1, 1] Python being an interpreted language works so differently than its other 2 siblings Ruby, JS. Surprised. -- Thanks, Arup Rakshit ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] Syntac Error
Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on win32 Type "copyright", "credits" or "license()" for more information. >>> print("Game Over") Game Over >>> Sent from Mail for Windows 10 Each time I write the program in Python from the book Python Programming, Third Edition, for the absolute beginner. When I run the program and get a syntac error invalid syntac that highlights the first 1 in 3.1.1 above. When I save the program and try to run it from the app on my screen it just blinks and goes back to the app on my desktop. Suggestions please. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Syntac Error
On 24/04/2019 00:31, Bob Griffin wrote: > Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] > on win32 > Type "copyright", "credits" or "license()" for more information. print("Game Over") > Game Over > When I run the program and get a syntac error invalid syntac that highlights > the first 1 in 3.1.1 above. OK, That suggests that there is a problem in the way you are trying to run the program. It seems like you are typing the Python 3.1.1 message as part of your code. That's not what you are supposed to do. When you start the Python interactive interpreter by typing py at the Windows command prompt (or using the Windows+"R" dialog) you should get a console window which already contains everything down to the first >>> You do not type that in. The only bit you type in is the print("game over") > When I save the program and try to run it from the app on > my screen it just blinks and goes back to the app on my desktop. I'm not sure which app you are referring to but I'll take a guess at what is happening. The interpreter is starting up, running until the error is displayed then closing again. All so fast that you cannot see it. Try adding a line like input("Hit Enter to quit") at the end of your code. So the whole program should just contain the two lines: print("Game over") input("Hit Enter to quit") Try running that. Hopefully you get the output Game over Hit Enter to quit HTH -- Alan G Author of the Learn to Program web site http://www.alan-g.me.uk/ http://www.amazon.com/author/alan_gauld Follow my photo-blog on Flickr at: http://www.flickr.com/photos/alangauldphotos ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor