TransWikia.com

Keywords in python that evaluate to themselves

Stack Overflow Asked by sawan on January 24, 2021

There’s a notion of keywords in Clojure where you define them by adding a colon in front of your word you are trying to address as a keyword. Also, it evaluates to itself. For example:

:my-keyword 
;=> :my-keyword

Is there any way to implement this in python by defining some custom class or any workarounds?

The reason for having this is to have more self-desctriptive parameters (strings are there, but one cannot keep track of having consistent strings while passing around).

A practical use case for this goes something like this:

def area(polygon_type):
  return 
  {
    "square": lambda side: (side * side),
    "triangle": lambda base, height: (0.5 * base * height)
  }[polygon_type]

area("square")(2) # ==> 4

But, having strings in such manner leads to error at runtime, if mishandled. But having something like keywords even an auto-complete feature in any IDE suggests the mistake that has been made while passing in the polygon_type.

area("Sqaure")(2) # would lead to a KeyError

Is there some feature in python that solves this type of problem, that I am unaware of?
If not, how’d someone go about tackling this?

Edit:
I am not trying to solve the problem of having such a function in particular; but instead looking for a way of implementing keyword concept in python. As, with enums I have to bundle up and explicitly define them under some category (In this case polygon_type)

One Answer

Keywords in Clojure are interned strings and Clojure provides special syntactic support for them. I suggest you take a look at how they are implemented. It seems like Python does some interning of its strings but I don't know much of its details.

The point of using keyword is fast comparisons and map lookup. Although I am not sure how you would benefit from it, you could try to implement your own keyword-like objects in Python using string interning, something like this:

str2kwd = {}

class Keyword:
    def __init__(self, s):
        self.s = s
        
    def __repr__(self):
        return str(self)
    
    def __str__(self):
        return ":" + self.s

def kwd(s):
    """Construct a keyword"""
    k = str2kwd.get(s)
    if k is None:
        k = Keyword(s)
        str2kwd[s] = k
    return k

Whenever you want to construct a keyword, you call the kwd function. For the Keyword class, we rely on the default equality and hash methods. Then you could use it like this:

>>> kwd("a")
:a
>>> kwd("a") == kwd("a")
True
>>> kwd("b") == kwd("a")
False
>>> kwd_a = kwd("a")
>>> kwd_b = kwd("b")
>>> {kwd_a: 3, kwd_b: 4}
{:a: 3, :b: 4}
>>> {kwd_a: 3, kwd_b: 4}[kwd_a]
3

However, I have not measured if this results in faster comparisons and map-lookups than just using regular Python strings, which is probably the most idiomatic choice for Python anyway. I doubt you would see a significant improvement in performance from using this home-made keyword class. Also note that it is best to call the kwd function at the top-level of the module and assign it to a variable that you use, instead of calling kwd everytime you need a keyword. Obviously, you will not have the special keyword syntax as in Clojure.

UPDATE: How to avoid misspelling bugs

If you are worried about misspelling keys in your map, you can assign the keys to local variables and use those local variables instead of the key values directly. This way, if you misspell a local variable name you will likely get an error much sooner because you are referring to a local variable that does not exist.

>>> kwd_square = "square"
>>> kwd_triangle = "triangle"
>>> m = {kwd_square: 3, kwd_triangle: 4}
>>> m[kwd_square]
3
>>> m[Square]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Square' is not defined

Answered by Rulle on January 24, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP