Re: [Tutor] a little loop

2013-05-27 Thread John Steedman
Some other tools, if you haven't come across them yet.

You already know about str.join ()

Slicing

>>>b=['s','p','a','m']
b [ : 1 ]
['s']
b [ : 2 ]
['s', 'p']

Also, consider

>>>len ( b)
4

>>>range ( 4 )
[ 0, 1, 2, 3, 4]
# which I can iterate over.












On Tue, May 28, 2013 at 4:54 AM, Tim Hanson  wrote:

> Okay, so I made it to FOR loops in the Lutz book.  A couple of days ago I
> was
> helped here with the .join method for creating strings from lists or
> tuples of
> strings.  I got to wondering if I could just, for the sake of learning, do
> the
> same thing in a FOR loop, since that's today's chapter:
>
> x=0; ham=''; b=['s','p','a','m'] #or, b=('s','p','a','m')
> for t in b:
> ham=ham+b[x]
> print(ham);x+=1
>
>
> s
> sp
> spa
> spam
>
> Alright, it works, eventually.  Can someone help me find a little more
> elegant
> way of doing this?  I'm sure there are several.
>
> Incidentally, I put the print statement within the FOR loop so I could
> watch
> progress.
> ___
> Tutor maillist  -  Tutor@python.org
> To unsubscribe or change subscription options:
> http://mail.python.org/mailman/listinfo/tutor
>
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] a little loop

2013-05-27 Thread kartik sundarajan
One way I can suggest is

x=0; ham=''; b=['s','p','a','m'] #or, b=('s','p','a','m')
> for t in b:
> ham=ham+b[x]
> print(ham);x+=1
>
>
> 't' is actually  equal to b[x] and its faster then indexed based look-up.
so you can rewrite

ham = ham + b[x]

as

ham += t

and remove the x increment.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


[Tutor] a little loop

2013-05-27 Thread Tim Hanson
Okay, so I made it to FOR loops in the Lutz book.  A couple of days ago I was 
helped here with the .join method for creating strings from lists or tuples of 
strings.  I got to wondering if I could just, for the sake of learning, do the 
same thing in a FOR loop, since that's today's chapter:

x=0; ham=''; b=['s','p','a','m'] #or, b=('s','p','a','m')
for t in b:
ham=ham+b[x]
print(ham);x+=1


s
sp
spa
spam

Alright, it works, eventually.  Can someone help me find a little more elegant 
way of doing this?  I'm sure there are several.

Incidentally, I put the print statement within the FOR loop so I could watch 
progress.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread eryksun
On Mon, May 27, 2013 at 4:01 PM, Jim Mooney  wrote:
> I was looking at the bytecode doc page as a break from the Lutz book,
> since I like Assembler-type code due to its total non-ambiguity, but
> the page doesn't say much. Is there a doc somewhere that corresponds
> some of the bytecode to Python source? I thought rot_1, 2, 3, and 4
> looked useful, but it would take awhile to disassemble random programs
> to see what source they come from.

Are you referring to the documentation of the dis module?

http://docs.python.org/2/library/dis

AFAIK, that's all the documentation there is. Heed the warning in bold
that bytecode is an implementation detail of the target virtual
machine. For example, Jython creates a .class file containing JVM
bytecode, and its code objects don't even have a co_code attribute...

I once manually instantiated a CPython 2.x code object to create a
very simple function with a default argument. Here was my first and
only attempt, FWIW:

from opcode import opmap
from types import FunctionType, CodeType
from inspect import CO_OPTIMIZED, CO_NEWLOCALS, CO_NOFREE

OP = type('OP', (), opmap)

def foo1(x='spam'):
print x

foo2 = FunctionType(
  CodeType(# func_code
1, # co_argcount
1, # co_nlocals
1, # co_stacksize
CO_OPTIMIZED | # co_flags
CO_NEWLOCALS |
CO_NOFREE,
str(bytearray([# co_code
  OP.LOAD_FAST, 0, 0,  # stack.push(fastlocals[0])
   # x is fastlocals[0]
  OP.PRINT_ITEM,   # print stack.pop()
  OP.PRINT_NEWLINE,
  OP.LOAD_CONST, 0, 0, # stack.push(co_consts[0])
   # co_consts[0] is None
  OP.RETURN_VALUE, # return stack.pop()
])),
(None,),   # co_consts
(),# co_names
('x',),# co_varnames
__file__,  # co_filename
'foo2',# co_name
10,# co_firstlineno
'\x00\x01',# co_lnotab,
   #   see: Objects/lnotab_notes.txt
(),# co_freevars
(),# co_cellvars
  ),
  globals(),   # func_globals
  'foo2',  # func_name
  ('spam',),   # func_defaults
  None,# func_closure
)


For example:

>>> foo2()
spam
>>> foo2('eggs')
eggs

There's an assembler package on PyPI that does this for real, if this
sort of thing interests you:

https://pypi.python.org/pypi/BytecodeAssembler

As to the VM's ROT operations, I'm not sure what you mean by seeing
"what source they come from". They simply manipulate the top 2, 3, or
4 items on the frame's stack:

ROT_TWO()
Swaps the two top-most stack items.

ROT_THREE()
Lifts second and third stack item one position up,
moves top down to position three.

ROT_FOUR()
Lifts second, third and forth stack item one position up,
moves top down to position four.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Dave Angel

On 05/27/2013 07:56 PM, ALAN GAULD wrote:

I can't speak for Jim, but I have used the dis module in the past to

quickly check how python parsed an expression (e.g. not x is y).
Yes, I've done that too. But that's not the bytecodes (at least not what

I understand as bytecodes) that's the dis-assembly listing which is
usually far more useful. And as you say the dis module provides that
without any need for other tools.




Certainly dis.dis() doesn't show the entire file, it shows only a single 
function.  And it doesn't show the const-table, or the local-table. 
(Whatever they're called in byte-code-speak)


But within the function it's probably got most all of what you would see 
in a byte code file, barring the hex values of the bytes themselves. 
Notice it shows you the offsets, so you know how many bytes each 
instruction takes.  And when the instruction is more than one byte long, 
it shows the operands, in both decimal form and in mnemonic.


When I first got my hands on the (very good) spec for the java byte code 
file, I wrote a disassembler for it.  Seems to me the python one is very 
analogous to that one.


Note, though that I have never tried to even look at the python one, 
even with a hex editor.  So I could be all wet.



--
DaveA
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Mark Lawrence

On 28/05/2013 00:32, Steven D'Aprano wrote:

On 28/05/13 06:01, Jim Mooney wrote:

Another question. I tried installing a package that back-compiles (in
win 7), so I could see things that way, and got the error
"Unable to find vcvarsall.bat"


Shall we guess what package that is? I love guessing games!

Ah, who am I kidding. No I don't.



Been there, seen it, done it, got the t-shirt 
http://old.nabble.com/Re%3A-Unable-to-install-PyWin32-with-Python-2.6.2-p25202902.html


--
If you're using GoogleCrap™ please read this 
http://wiki.python.org/moin/GoogleGroupsPython.


Mark Lawrence

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread ALAN GAULD
I can't speak for Jim, but I have used the dis module in the past to
>quickly check how python parsed an expression (e.g. not x is y).
>Yes, I've done that too. But that's not the bytecodes (at least not what 
I understand as bytecodes) that's the dis-assembly listing which is 
usually far more useful. And as you say the dis module provides that
without any need for other tools.

Alan G.___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Steven D'Aprano

On 28/05/13 06:01, Jim Mooney wrote:

I was looking at the bytecode doc page as a break from the Lutz book,
since I like Assembler-type code due to its total non-ambiguity, but
the page doesn't say much. Is there a doc somewhere that corresponds
some of the bytecode to Python source? I thought rot_1, 2, 3, and 4
looked useful, but it would take awhile to disassemble random programs
to see what source they come from.


Python byte-code is not officially documented, and is subject to change.
But it should be relatively straight-forward to understand, the names
are mostly self-explanatory. The main thing to remember is that the
byte-code is reverse-polish stack-based, like Forth, Postscript, and
most Hewlett-Packard scientific calculators.

http://en.wikipedia.org/wiki/Stack-oriented_programming_language



Another question. I tried installing a package that back-compiles (in
win 7), so I could see things that way, and got the error
"Unable to find vcvarsall.bat"


Shall we guess what package that is? I love guessing games!

Ah, who am I kidding. No I don't.


--
Steven
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Devin Jeanpierre
On Mon, May 27, 2013 at 7:20 PM, Alan Gauld  wrote:
> Can you give a use case of how you think you could use them?
> There may be another way to do what you want.

I can't speak for Jim, but I have used the dis module in the past to
quickly check how python parsed an expression (e.g. not x is y).

-- Devin
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Fwd: QT Python: How to re-use the return value of fileDialog.openFileName() ?

2013-05-27 Thread SM
Thank you, for the details.


On Mon, May 27, 2013 at 10:13 AM, eryksun  wrote:

> On Mon, May 27, 2013 at 9:45 AM, Matthew Ngaha 
> wrote:
> > On Mon, May 27, 2013 at 2:14 PM, SM  wrote:
> >
> >>But then I also had to use self.fileDialog from within the function. Not
> >> sure how I could avoid using fileDialog.
> >>
> >
> > No problem. to do it without the instance variable, you access its
> > method directly. so replace:
> >
> > path = self.fileDialog.getOpenFileName()
> >
> > with
> >
> > path = QFileDialog.getOpenFileName(self)
> >
> > which is basically the same thing except you're going to the method
> > directly instead of creating a class instance. The self argument may
> > or may not be needed, its been a while since ive used Qt.
>
> getOpenFileName is a static method. The first parameter is the
> [optional] parent widget. The documentation says the dialog should be
> centered over the parent, but it probably won't be if you let it use
> the default native dialog. This can be disabled with the option
> QFileDialog.DontUseNativeDialog. For example:
>
> path = QtGui.QFileDialog.getOpenFileName(
> parent=self,
> caption='File Dialog',
> directory='path/to/open',
> filter='type1 (*.ext1);;type2 (*.ext2)',
> selectedFilter='type2 (*.ext2)',
> options=QFileDialog.DontUseNativeDialog,
> )
>
> http://pyqt.sourceforge.net/Docs/PyQt4/qfiledialog.html#getOpenFileName
> http://pyqt.sourceforge.net/Docs/PyQt4/qfiledialog.html#Option-enum
>
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Alan Gauld

On 27/05/13 21:21, Jim Mooney wrote:

Oscar Benjamin 


What do you want these for? I've never needed to know what the
interpreter's bytecodes are.


I programmed A86 Assembler years ago, and just find the bytecode has a
comfortable clarity as a learning tool,


To me, the bytecodes are the literal hex values corresponding
to the Python "assembler" statements. Are you sure you need the 
bytecodes? You can use the standard library to generate the

assembler listing from the Python code. Like Oscar I'm not sure
how the bytecodes would help?

I'm an ex assembler hacker myself but I've never found a need
for the Python bytecodes. Its not like you ever need to translate
the binary into assembler (the usual reason for reading bytecodes) since 
you usually have the source and from that you can get the dis-assembly 
listing. If you don't have the source it probably

means it's a C module and you need to look at the native machine
code disassembly not the Python one.

Can you give a use case of how you think you could use them?
There may be another way to do what you want.

--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Jim Mooney
Oscar Benjamin 

> What do you want these for? I've never needed to know what the
> interpreter's bytecodes are.

I programmed A86 Assembler years ago, and just find the bytecode has a
comfortable clarity as a learning tool, at least for me. When an
author vaguely tried to explain that a Name in Python is not the Data,
it confused me at first what he meant. But I could literally see the
disjunction in bytecode. I'm sure it's not for everyone - just aging
Assembler hackers ;')

Thanks for the Python(x,y) tip. That's probably useful for other stuff, too.

-- 
Jim Mooney

Atlantis Anyone?
http://www.pnas.org/content/early/2013/05/17/1301760110.abstract?sid=8313cec8-3ee0-47a0-9488-5bc33983b081
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Oscar Benjamin
On 27 May 2013 21:01, Jim Mooney  wrote:
>
> I was looking at the bytecode doc page as a break from the Lutz book,
> since I like Assembler-type code due to its total non-ambiguity, but
> the page doesn't say much. Is there a doc somewhere that corresponds
> some of the bytecode to Python source? I thought rot_1, 2, 3, and 4
> looked useful, but it would take awhile to disassemble random programs
> to see what source they come from.

What do you want these for? I've never needed to know what the
interpreter's bytecodes are.

> Another question. I tried installing a package that back-compiles (in
> win 7), so I could see things that way, and got the error
> "Unable to find vcvarsall.bat"

I don't know what you mean by "back-compiles" but I guess this package
contains extension modules in C. To install that from source you will
need a C compiler. The project may provide an installer with
pre-compiled binaries for Windows (check on their download page).

> Looking around it said I needed to install Visual Studio (Express, of
> course ;'). I'm on a very slow connection, right now. Is there any
> alternative to downloading this monster 600 Mb ISO just to install a
> package, or am I doomed to getting Visual Studio?

You can use MinGW instead of Visual Studio but you will need to apply
the fix to cygwinccompiler.py described here:
http://stackoverflow.com/questions/6034390/compiling-with-cython-and-mingw-produces-gcc-error-unrecognized-command-line-o

Otherwise you could install Python(x, y). This will install Python,
MinGW and a bunch of other useful packages in one go:
https://code.google.com/p/pythonxy/wiki/Downloads?tm=2


Oscar
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


[Tutor] bytecode primer, and avoiding a monster download

2013-05-27 Thread Jim Mooney
I was looking at the bytecode doc page as a break from the Lutz book,
since I like Assembler-type code due to its total non-ambiguity, but
the page doesn't say much. Is there a doc somewhere that corresponds
some of the bytecode to Python source? I thought rot_1, 2, 3, and 4
looked useful, but it would take awhile to disassemble random programs
to see what source they come from.

Another question. I tried installing a package that back-compiles (in
win 7), so I could see things that way, and got the error
"Unable to find vcvarsall.bat"

Looking around it said I needed to install Visual Studio (Express, of
course ;'). I'm on a very slow connection, right now. Is there any
alternative to downloading this monster 600 Mb ISO just to install a
package, or am I doomed to getting Visual Studio?

-- 
Jim Mooney

Atlantis Anyone?
http://www.pnas.org/content/early/2013/05/17/1301760110.abstract?sid=8313cec8-3ee0-47a0-9488-5bc33983b081
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Buttons

2013-05-27 Thread Alan Gauld

On 27/05/13 15:17, Jack Little wrote:

Is there a way to make buttons a little like a raw_input in the context
when the user presses the button, the program performs a function?


Yes, but its a lot more complicated than raw_input.

See the GUI topic of my tutorial for examples.

Also check out the EasyGUI package for an easy way to add GUI style 
dialog boxes to an otherwise console based app. (see my "Talking to the 
User" topic in my V3 tutorial for more on EasyGUI)


HTH
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Fwd: QT Python: How to re-use the return value of fileDialog.openFileName() ?

2013-05-27 Thread eryksun
On Mon, May 27, 2013 at 9:45 AM, Matthew Ngaha  wrote:
> On Mon, May 27, 2013 at 2:14 PM, SM  wrote:
>
>>But then I also had to use self.fileDialog from within the function. Not
>> sure how I could avoid using fileDialog.
>>
>
> No problem. to do it without the instance variable, you access its
> method directly. so replace:
>
> path = self.fileDialog.getOpenFileName()
>
> with
>
> path = QFileDialog.getOpenFileName(self)
>
> which is basically the same thing except you're going to the method
> directly instead of creating a class instance. The self argument may
> or may not be needed, its been a while since ive used Qt.

getOpenFileName is a static method. The first parameter is the
[optional] parent widget. The documentation says the dialog should be
centered over the parent, but it probably won't be if you let it use
the default native dialog. This can be disabled with the option
QFileDialog.DontUseNativeDialog. For example:

path = QtGui.QFileDialog.getOpenFileName(
parent=self,
caption='File Dialog',
directory='path/to/open',
filter='type1 (*.ext1);;type2 (*.ext2)',
selectedFilter='type2 (*.ext2)',
options=QFileDialog.DontUseNativeDialog,
)

http://pyqt.sourceforge.net/Docs/PyQt4/qfiledialog.html#getOpenFileName
http://pyqt.sourceforge.net/Docs/PyQt4/qfiledialog.html#Option-enum
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


[Tutor] Buttons

2013-05-27 Thread Jack Little
Is there a way to make buttons a little like a raw_input in the context when 
the user presses the button, the program performs a function?___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Fwd: QT Python: How to re-use the return value of fileDialog.openFileName() ?

2013-05-27 Thread SM
Yes, the following works.
path = QtGui.QFileDialog.getOpenFileName()

Thanks!


On Mon, May 27, 2013 at 9:45 AM, Matthew Ngaha  wrote:

> On Mon, May 27, 2013 at 2:14 PM, SM  wrote:
>
> >But then I also had to use self.fileDialog from within the function. Not
> > sure how I could avoid using fileDialog.
> >
> > Thanks.
>
> No problem. to do it without the instance variable, you access its
> method directly. so replace:
>
> path = self.fileDialog.getOpenFileName()
>
> with
>
> path = QFileDialog.getOpenFileName(self)
>
> which is basically the same thing except you're going to the method
> directly instead of creating a class instance. The self argument may
> or may not be needed, its been a while since ive used Qt.
>
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Fwd: QT Python: How to re-use the return value of fileDialog.openFileName() ?

2013-05-27 Thread Matthew Ngaha
On Mon, May 27, 2013 at 2:14 PM, SM  wrote:

>But then I also had to use self.fileDialog from within the function. Not
> sure how I could avoid using fileDialog.
>
> Thanks.

No problem. to do it without the instance variable, you access its
method directly. so replace:

path = self.fileDialog.getOpenFileName()

with

path = QFileDialog.getOpenFileName(self)

which is basically the same thing except you're going to the method
directly instead of creating a class instance. The self argument may
or may not be needed, its been a while since ive used Qt.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Fwd: QT Python: How to re-use the return value of fileDialog.openFileName() ?

2013-05-27 Thread SM
Thanks!
I defined a function, as you suggested, to call when the button was
clicked. But then I also had to use self.fileDialog from within the
function. Not sure how I could avoid using fileDialog.

I also defined global variable (under the class) and assigned it to the
filename, so I could access it from within the main class.

The following works:

 QtCore.QObject.connect(self.toolButton,
QtCore.SIGNAL(_fromUtf8("clicked()")), self.getFileName)


 def getFileName(self):
self.fileDialog = QtGui.QFileDialog()
path = self.fileDialog.getOpenFileName()
print(" File Selected: ", path)
self.lineEdit.setText(path)
self.XmlFileName = path


Thanks.


On Mon, May 27, 2013 at 6:01 AM, Matthew Ngaha  wrote:

> Sorry for the forward, i forgot to reply to tutor
>
> On Mon, May 27, 2013 at 3:53 AM, Sunitha Misra 
> wrote:
>
> > self.fileDialog = QtGui.QFileDialog()
> >
> > QtCore.QObject.connect(self.toolButton,
> > QtCore.SIGNAL(_fromUtf8("clicked()")), self.fileDialog.getOpenFileName)
> >
>
> i think the way youre doing it won't allow you to get the filename as
> you are calling it through a signal.
>
> Things you can do:
>
> 1) dont use self.fileDialog.getOpenFileName as a slot like you are
> doing. Create a function and call it when the toolbar's button is
> clicked, then in that function assign the filename to the return value
> of self.fileDialog.getOpenFileName.
>
> 2) if the only thing your self.fileDialog is doing is responding to
> the toolbar's button, don't create it at all. do what i said in #1 and
> assign a filename variable to the full method of the dialog. What im
> saying is there might be no need to create the self.fileDialog
> instance variable at all.
> ___
> Tutor maillist  -  Tutor@python.org
> To unsubscribe or change subscription options:
> http://mail.python.org/mailman/listinfo/tutor
>
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor


[Tutor] Fwd: QT Python: How to re-use the return value of fileDialog.openFileName() ?

2013-05-27 Thread Matthew Ngaha
Sorry for the forward, i forgot to reply to tutor

On Mon, May 27, 2013 at 3:53 AM, Sunitha Misra  wrote:

> self.fileDialog = QtGui.QFileDialog()
>
> QtCore.QObject.connect(self.toolButton,
> QtCore.SIGNAL(_fromUtf8("clicked()")), self.fileDialog.getOpenFileName)
>

i think the way youre doing it won't allow you to get the filename as
you are calling it through a signal.

Things you can do:

1) dont use self.fileDialog.getOpenFileName as a slot like you are
doing. Create a function and call it when the toolbar's button is
clicked, then in that function assign the filename to the return value
of self.fileDialog.getOpenFileName.

2) if the only thing your self.fileDialog is doing is responding to
the toolbar's button, don't create it at all. do what i said in #1 and
assign a filename variable to the full method of the dialog. What im
saying is there might be no need to create the self.fileDialog
instance variable at all.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor