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


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] 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 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


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