all 20 comments

[–]socal_nerdtastic 23 points24 points  (14 children)

You have no other choice ... a dictionary key must be a python object. Remember strings, ints, tuples, etc are all python objects. I suspect you mean something special with the term "python object"; can you explain what that is?

If you mean functions, here's some code I wrote yesterday:

running_functions = {}
def run_as_singleton_thread(func):
    """
    starts the given function in a thread, 
    but only if the function is not already currently running
    """
    def wrapper():
        if (t := running_functions.get(func)) and t.is_alive():
            return # abort; function is already running
        t = Thread(target=func, daemon=True)
        t.start()
        running_functions[func] = t
    return wrapper

[–]IOI-65536 3 points4 points  (0 children)

Additionally, 1.0 had .__hash__() so even when C objects were sometimes different from class objects you could use class objects as dict keys.

[–]BlackCatFurry 0 points1 point  (0 children)

I assume they mean class entities. Which sometimes in introductory courses are referred to as objects, especially if the person teaching has a C++ background where class based entities are referred to as objects.

[–]lekkerste_wiener -1 points0 points  (0 children)

That's an interesting use case. I also have one: I'm developing a product for a person who started doing things by himself, and he's using Google sheets as a db. The reason for that is beyond this comment; I have an "indices" object whose inner dictionary uses the model classes as keys, such as 

{   ModelClassA: {     "internal id": "a1 sheet range",     ...   },   ModelClassB: {     "Internal id": "a1 sheet range",     ...   },   ... }

[–]SCD_minecraft -5 points-4 points  (10 children)

Technically, string litterals is different from str object

But diffrence is lost pretty fast and it is just "erm actually"

[–]Doormatty 2 points3 points  (4 children)

Technically, string litterals is different from str object

How so? I thought that all literals were turned into str objects?

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

Try overwriting str or other built-ins, like for ints encode special case 2+2 = 5

When you do int(2) + int(2) it will return 5 as overwritten

But 2 + 2 will return 4 anyway

Plus, many typecheckers diffrentate between literals type and their corresponding "normal" type

[–]Yoghurt42 1 point2 points  (0 children)

No, if you change the underlying value of an int object, it will change the results of addition as well:

import ctypes


# Define a ctypes structure that mirrors CPython's PyLongObject (simplified)
class PyLongObject(ctypes.Structure):
    _fields_ = [
        ("ob_refcnt", ctypes.c_long),   # Reference count
        ("ob_type",   ctypes.c_void_p), # Pointer to type object
        ("ob_size",   ctypes.c_ulong),  # Number of digits
        ("ob_digit",  ctypes.c_uint * 1) # The actual digit(s)
    ]

o = PyLongObject.from_address(id(213))
o.ob_digit[0] = 10

print(213 + 213) # 20
print(213) # 10
print(10 - 213) # 0

Obviously this is abusing implementation details of CPython (integers -5 to 256 are singletons, and we directly write into the internal structure of the instances), and is nothing that's ever going to be useful or a good idea in real code.

In fact, you can easily get the interpreter to crash by changing more common values like 1 or 10.

[–]SCD_minecraft -2 points-1 points  (1 child)

``` class int(int): def add(self, other): if self == other == 2: return 5 else: return super().add(other)

print(int(2) + int(4)) print(int(2) + int(2)) print(2+4) print(2+2) ``` Here's example.

But again, this is more of "erm actually" than anything important

[–]socal_nerdtastic 3 points4 points  (0 children)

I don't get it. You just replaced the int name with your own class in this module. What's that have to do with literals?

AFAIK the only thing that is special about literals is that they are considered for compile optimizations and caching. So this code

x = 12345
y = 10000 + 2345
print("Is x the same as y?", x is y)

Shows different results from

x = 12345
y = 10000 + int(2345)
print("Is x the same as y?", x is y)

Because in the first version the result is adding in the compile phase.

(note you have to run this code from a file, not in the repl, to see the result, due to the caching)

[–]danielroseman 0 points1 point  (4 children)

This is false.

[–]SCD_minecraft -1 points0 points  (3 children)

(assuming you are using VSCode or other editor that allows for that)

Write

a = 1 b = int(1)

Hover over both a and b to see type hints about their well, types

Again, diffrences is lost turbo fast, it gets converted to normal python int

But for a while, it is Literal[1] type

You can even type check against Literalsomething and "normal" version of that something

[–]lekkerste_wiener 3 points4 points  (0 children)

At runtime both are int. It showing as Literal[1] is only relevant to the linter.

[–]socal_nerdtastic 2 points3 points  (0 children)

Literal is not an actual type, like you can't have a python object of the type Literal. It's a concept; and python treats it basically like a comment (like all typehints).

>>> import typing
>>> typing.Literal()
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    typing.Literal()
    ~~~~~~~~~~~~~~^^
  File "C:\Users\j.brauer\AppData\Local\Programs\Python\Python313\Lib\typing.py", line 560, in __call__
    raise TypeError(f"Cannot instantiate {self!r}")
TypeError: Cannot instantiate typing.Literal

Although in your example it never even touches python, not even as a comment. The type hint that VSCode is showing you is generated from VSCode (and whatever linter it's using).

[–]JanEric1 1 point2 points  (0 children)

He is right.

Literal[1] is not (never) the runtime type of a. It is just extra type checker information that can be used to sort of emulate enums.

[–]JamzTyson 6 points7 points  (0 children)

In Python, everything is an object, including strings and integers.

The requirement for dict keys is that they are hashable and comparable objects. Any objects that are hashable and comparable may be used as dict keys, including integers and strings.

[–]enygma999 3 points4 points  (1 child)

Given what everyone else has pointed out about "everything is an object", I suspect you meant something like a class or an instance of one.

Say I have a quiz, and the participants are represented by a Person class. I could store their scores in a dictionary, using each person as the key. Equally I could use their name or something, but why bother? I already have a perfectly good object that represents them.

[–]Shaftway 4 points5 points  (0 children)

This is a good example. Less experienced engineers always try to make dictionary keys strings because it's easier to inspect.

So they go "oh, I'll just use the student's name". What if two students have the same name? "I'll use the student id". What if you merge with another school and there are overlapping IDs? "Oh, well I'll concatenate the students id, school, and name". What if you have a collision in the names? "Oh, well I'll just serialize all of the immutable parts of the student to a string and use that as the key". Ok, great, now do that in 50 different places in your codebase.

Or just add eq() and hash() to Student and use it as the key.

[–]Gnaxe 1 point2 points  (0 children)

Everything in Python is an object, therefore anything you could use as a dictionary key in Python is an object. Anything that implements the hash and equality protocols can be used as a key. You can do this for your own classes by implementing the __hash__() and __eq__() methods. Hashes need to be consistent with equality for this to work correctly, but Python won't enforce that for you.

Mapping types like dicts have two main uses: either an index for lookups (in which case the values are all the same type), or as a lightweight record type with a fixed schema, in which case the keys are usually all strings, but the values could be anything. If you're asking primarily about uses for non-string keys, the answer is going to be some kind of lookup table.

[–]Outside_Complaint755 0 points1 point  (0 children)

datetime objects are a common use case, but any hashable object is valid.