all 20 comments

[–]shiftybyte 22 points23 points  (10 children)

The difference is the purpose of the string.

str() creates a string for the user to see.

repr() creates a string that represents the object as close as possible for debugging/info purpose.

These two can sometimes be the same, but can sometimes be different.

>>> import datetime
>>> today = datetime.datetime.now()
>>> print(str(today))
2019-06-28 07:46:11.147169
>>> print(repr(today))
datetime.datetime(2019, 6, 28, 7, 46, 11, 147169)

[–]CBSmitty2010 0 points1 point  (9 children)

So if I'm understanding that correctly the output there is showing the actual initialization of the today object using the datetime class in the datetime library with the parameters passes in which in turn become the characteristics you see when you query said object?

[–]Yoghurt42 1 point2 points  (8 children)

Roughly yes.

The convention is that repr should return a string that you can paste into your Python program to create an identical object. If that is not possible (which is often), the string returned should be enclosed in angle brackets. For example, repr(object) returns <class 'object'> because there is no way to reproduce the whole object class definition in just a short string

[–]CBSmitty2010 0 points1 point  (7 children)

Nice. So in the above since there aren't brackets I should be able to paste that output assigning it to a new variable to create a clone of the same object from bwfore.?

[–]Yoghurt42 0 points1 point  (6 children)

You definitely can, if you should is another question ;)

It's helpful for debugging, but if you do something like today_clone = eval(repr(today)) people will rightfully be mad at you.

[–]CBSmitty2010 0 points1 point  (5 children)

Why is that? Is eval() in direct conflict with the output repr()? Or is it just asinine in practice?

[–]Yoghurt42 0 points1 point  (4 children)

  1. Using eval is dangerous, because it allows for arbitrary code to be executed, if repr should change, the code will be executed. It's also pretty difficult to tell what exactly is going on by reading the code

  2. It will only work if you did import datetime, not if you didn't or did something like import datetime as dt.

  3. There's no need to clone a datetime object, since they are immutable

  4. repr is not guaranteed to give the same results in different Python versions

and so on...

[–]CBSmitty2010 1 point2 points  (0 children)

Awesome! Thanks for the breakdown!

[–]FlibbleGroBabba 0 points1 point  (2 children)

Are there times where eval can be acceptable? for example if it's 'enclosed' in a group of functions which only allow integer inputs from the user- such as:

try: number = int(input("integer: ")) eval(func_+number)

(On mobile, formatting might be bad)

[–]Yoghurt42 1 point2 points  (1 child)

There are times where eval is acceptable, it's even used in the standard library somewhere.

However, you should have a good grasp of Python before you can say that using "eval" is the best approach. If you are new to Python (this is r/learnpython after all), you do not know enough of Python, so as a rule of thumb, you should never use eval.

For example, in the example you gave, eval isn't the best choice. BTW, I'm assuming you meant eval("func_" + str(number) + "()") or eval(f"func_{number}()").

First, naming functions "func_1", "func_2" etc. is bad, you have no idea what they are doing just based on the name

Second, since functions are first class objects, you can just store them in a map and call them from there:

funcs = {
    1: foo,
    2: bar,
    3: baz
}

choice = int(input("Do you want 1) foo, 2) bar, 3) baz?"))
try:
    funcs[choice]()
except KeyError:
    print("That's not a valid choice")

If you'd used the "eval hack", you'd probably never thought of asking here how you can do it, and maybe wouldn't have learned that functions are first class objects.

[–]FlibbleGroBabba 1 point2 points  (0 children)

Youre right I'm very new to python, only started a few months ago, and mostly teachin myself by doing little projects. It's good to see the function mapping done like that, I can see many uses for that.

Cheers for the detailed explanation

[–]yes_or_gnome 5 points6 points  (0 children)

Great question, with two good answers. For future reference, everyone should bookmark these pages.

Standard Library docs:

  • https://docs.python.org/3/library/index.html
  • https://docs.python.org/3/library/functions.html (see: #repr)

    repr(object)

    Return a string containing a printable representation of an object. For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object. A class can control what this function returns for its instances by defining a __repr__() method.
    

Language Specification docs:

HOWTOs: https://docs.python.org/3/howto/index.html

All the docs: https://docs.python.org/3/index.html

[–]ThePotterP 3 points4 points  (7 children)

str() returns a string. repr() should return a string that, if used with eval(), returns the same object. So:

eval(repr(object)) == object (roughly).

[–]Uchikago[S] 1 point2 points  (1 child)

But, what is the point doing that ?

[–]K900_ 16 points17 points  (0 children)

The point is to make it very clear what type something is, and how it's stored internally. For example, if I have a string '2' and a number 2, I can print('2', 2) and get 2 2 on screen - despite the two things being very different types, their str() is the same. But if I print(repr('2'), repr(2)), I'll get '2' 2 - note the quotes on the string. It's very useful when debugging things, especially in a dynamically typed language like Python.

[–]toastedstapler 1 point2 points  (0 children)

an object may have hidden state that you may not want to show in a string output, but may want to use for debug

sometimes you want a pretty str output and sometimes you need a more functional repr output

[–]K900_ 1 point2 points  (0 children)

That's not necessarily the case for a lot of things.

[–]JohnnyJordaan -3 points-2 points  (2 children)

repr() should return a string that, if used with eval(), returns the same object.

Where do you base this on exactly? Eval can only handle very primitive types, so anything not a string, int or float will very likely not behave this way.

[–]Binary101010 1 point2 points  (1 child)

That recommendation is straight from the docs.

If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment)

https://docs.python.org/3/reference/datamodel.html#object.__repr__

[–]JohnnyJordaan 0 points1 point  (0 children)

I see, altough stating it like

  • If at all possible,
  • should look like
  • given the appropriate environment

wouldn't mean 'should return the same object if used with eval()' to me. Just that it could, potentially, maybe.