[Tutor] Question on implmenting __getitem__ on custom classes

2019-04-23 Thread Arup Rakshit

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

2019-04-23 Thread Alan Gauld via Tutor
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

2019-04-23 Thread Steven D'Aprano
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

2019-04-23 Thread Arup Rakshit

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__ ?

2019-04-23 Thread Arup Rakshit
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

2019-04-23 Thread Mats Wichmann
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

2019-04-23 Thread Steven D'Aprano
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__ ?

2019-04-23 Thread Steven D'Aprano
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__ ?

2019-04-23 Thread Peter Otten
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__ ?

2019-04-23 Thread Alan Gauld via Tutor
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

2019-04-23 Thread Arup Rakshit

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

2019-04-23 Thread Bob Griffin
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

2019-04-23 Thread Alan Gauld via Tutor
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