0

Like the topic, is there a (quick) way, possibly a notation, to achieve the same effect as in turbo pascal to rapid make a list of all elements containing and between 'A' and 'Z'.

In turbo pascal it could be written as ['A'..'Z']

Michael Marx
  • 104
  • 8
HermDP
  • 205
  • 1
  • 6
  • Hmm, off the top of my head, there's `[chr(x) for x in range(b'A'[0], b'Z'[0])] ` but there's got to be something more elegant than that.... ` [chr(x) for x in range(ord('A'), ord('Z'))]` is slightly more readable... – Max Jul 10 '23 at 20:02
  • 1
    However, depending on what your use case is, there may be different solutions, like using a regular expression. What's the problem you're trying to solve? – Max Jul 10 '23 at 20:03
  • 1
    A solution for that exact list, but completely not generic, is `list(string.ascii_uppercase)`. Depends really whether you're looking for that precise list, or using it as an example of a more generic requirement. – slothrop Jul 10 '23 at 20:05
  • @max Use case is to write my code a little bit eleganter, suddenly I remembered in TP there was an elegant way to write those things, so I wondered what the most elegant/pythonic way is to write those things. – HermDP Jul 10 '23 at 20:08
  • A lot depends on what you want to do next. Iterate through the list? Check membership? – DYZ Jul 10 '23 at 20:09
  • Python doesn't have a char type - `'A'` and so on are just strings of length 1 - so it doesn't lend itself to doing this so concisely. – slothrop Jul 10 '23 at 20:09
  • @DYZ check if the first char of a variable is in the list. So I can run two independent programs to spread load, or spread network load (different ip adresses) depending on the first char of a specific var. For example program 1 : ['A'..'D'] program 2 : ['E'..'Z'] etc – HermDP Jul 10 '23 at 20:11
  • 4
    You could just compare strings: `if 'A' <= some_string[0] <= 'D':` for example. And you can do that without needing to materialize all the intermediate letters into a collection. – slothrop Jul 10 '23 at 20:13
  • @slothrop : good point ! most of the time I did something like if some_string[0] in ['A'..'F'] etc – HermDP Jul 10 '23 at 20:15

4 Answers4

3

I think the most elegant, simple and pythonic way is to use string module:

import string
print(string.ascii_uppercase)
>>> 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
x = list(string.ascii_uppercase)
print(x)
>>> ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
Soren V. Raben
  • 179
  • 1
  • 14
2

Unfortunately, there's no other way to do this in Python that's as compact and elegant. As @Soren said, for letters, you can use string.ascii_uppercase or string.ascii_lowercase, on which you can do slicing. For instance, print(string.ascii_uppercase[3:7]) prints "DEFG".

If you want something more generic, more readable, and not limited to latin alphabet letters, you have to write a class to approach what you want. I wrote this very simple example as a proof of concept (it misses many little details). It should work with any iterable (even though I've only tested it with strings).

class Slicer:
    def __init__(self, content):
        self.content = content
    
    def __getitem__(self, key: slice):
        if key.step is not None:
            return self.content[self.content.index(key.start) : self.content.index(key.stop)+1 : key.step]
        return self.content[self.content.index(key.start) : self.content.index(key.stop)+1]
    
    def __repr__(self):
        return f"Slicer({self.content!r})"

import string
letters = Slicer(string.ascii_uppercase)
print(letters)
# Slicer('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
print(letters["A":"H"])
# ABCDEFGH
print(letters["A":"H":2])
# ACEG

If you want to dig further, it uses the slice object in Python, representing sequences of objects: https://docs.python.org/3/glossary.html#term-slice

For numbers, you can just use the range() function: list(range(1, 7)) returns [1, 2, 3, 4, 5, 6] (it also supports steps).

Michael Marx
  • 104
  • 8
2

You can use map on a range of character numbers:

*letters, = map(chr,range(65,91)) # A-Z

print(letters)

['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

You can combine multiple ranges using unpacking:

*alphanum, = *map(chr,range(65,91)), *"0123456789", *map(chr,range(97,122))

Alternatively you could create a shorthand function of you own:

def CHR(a,*b):
    return [*map(chr,range(ord(a[0]),ord(a[-1])+1))] + (CHR(*b) if b else [])

Which you can reuse as needed:

codeChars = CHR('A..Z','-','0..9')

print(codeChars)

['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
 'Y', 'Z', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8',
 '9']
Alain T.
  • 40,517
  • 4
  • 31
  • 51
0

If you have icu4c and PyICU available, it is possible to construct a list of characters using Unicode sets:

from icu import UnicodeSet
chars = list(UnicodeSet('[A-Z]'))
print(chars)  
# ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']                                                          

Using Unicode sets, a lot more sophisticated and complex sets can be developed. For instance, all uppercase Latin script letters:

upper_latin = list(UnicodeSet('[[\p{Lu}] & [\p{Script=Latn}]]'))

Andj
  • 481
  • 3
  • 8