Skip to content

Commit

Permalink
Dropped Python 2 support, added type hinting.
Browse files Browse the repository at this point in the history
  • Loading branch information
jfjlaros committed Dec 19, 2020
1 parent fd6b9bf commit 732992d
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 136 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
@@ -1,6 +1,7 @@
language: python
python:
- "2.7"
- "3.5"
- "3.6"
- "3.7"
install: pip install tox-travis
script: tox
125 changes: 61 additions & 64 deletions dict_trie/dict_trie.py
@@ -1,16 +1,9 @@
import sys


if sys.version_info.major < 3:
from itertools import imap as map


def _add(root, word, count):
def _add(root: dict, word: str, count: int) -> None:
"""Add a word to a trie.
:arg dict root: Root of the trie.
:arg str word: A word.
:arg int count: Multiplicity of `word`.
:arg root: Root of the trie.
:arg word: A word.
:arg count: Multiplicity of `word`.
"""
node = root

Expand All @@ -24,13 +17,13 @@ def _add(root, word, count):
node[''] += count


def _find(root, word):
def _find(root: dict, word: str) -> dict:
"""Find the node after following the path in a trie given by {word}.
:arg dict root: Root of the trie.
:arg str word: A word.
:arg root: Root of the trie.
:arg word: A word.
:returns dict: The node if found, {} otherwise.
:returns: The node if found, {} otherwise.
"""
node = root

Expand All @@ -42,14 +35,14 @@ def _find(root, word):
return node


def _remove(node, word, count):
def _remove(node: dict, word: str, count: int) -> bool:
"""Remove a word from a trie.
:arg dict node: Current node.
:arg str word: Word to be removed.
:arg int count: Multiplicity of `word`, force remove if this is -1.
:arg node: Current node.
:arg word: Word to be removed.
:arg count: Multiplicity of `word`, force remove if this is -1.
:returns bool: True if the last occurrence of `word` is removed.
:returns: True if the last occurrence of `word` is removed.
"""
if not word:
if '' in node:
Expand All @@ -71,14 +64,14 @@ def _remove(node, word, count):
return result


def _iterate(path, node, unique):
def _iterate(path: str, node: dict, unique: bool) -> iter:
"""Convert a trie into a list.
:arg str path: Path taken so far to reach the current node.
:arg dict node: Current node.
:arg bool unique: Do not list multiplicities.
:arg path: Path taken so far to reach the current node.
:arg node: Current node.
:arg unique: Do not list multiplicities.
:returns iter: All words in a trie.
:returns: All words in a trie.
"""
if '' in node:
if not unique:
Expand All @@ -92,14 +85,14 @@ def _iterate(path, node, unique):
yield result


def _fill(node, alphabet, length):
def _fill(node: dict, alphabet: tuple, length: int) -> iter:
"""Make a full trie using the characters in {alphabet}.
:arg dict node: Current node.
:arg tuple alphabet: Used alphabet.
:arg int length: Length of the words to be generated.
:arg node: Current node.
:arg alphabet: Used alphabet.
:arg length: Length of the words to be generated.
:returns iter: Trie containing all words of length {length} over alphabet
:returns: Trie containing all words of length {length} over alphabet
{alphabet}.
"""
if not length:
Expand All @@ -111,16 +104,18 @@ def _fill(node, alphabet, length):
_fill(node[char], alphabet, length - 1)


def _hamming(path, node, word, distance, cigar):
def _hamming(
path: str, node: dict, word: str, distance: int, cigar: str) -> iter:
"""Find all paths in a trie that are within a certain hamming distance of
{word}.
:arg str path: Path taken so far to reach the current node.
:arg dict node: Current node.
:arg str word: Query word.
:arg int distance: Amount of allowed errors.
:arg path: Path taken so far to reach the current node.
:arg node: Current node.
:arg word: Query word.
:arg distance: Amount of allowed errors.
:arg cigar: CIGAR string.
:returns iter: All words in a trie that have Hamming distance of at most
:returns: All words in a trie that have Hamming distance of at most
{distance} to {word}.
"""
if distance < 0:
Expand All @@ -145,16 +140,18 @@ def _hamming(path, node, word, distance, cigar):
yield result


def _levenshtein(path, node, word, distance, cigar):
"""Find all paths in a trie that are within a certain Levenshtein
distance of {word}.
def _levenshtein(
path: str, node: dict, word: str, distance: int, cigar: str) -> iter:
"""Find all paths in a trie that are within a certain Levenshtein distance
of {word}.
:arg str path: Path taken so far to reach the current node.
:arg dict node: Current node.
:arg str word: Query word.
:arg int distance: Amount of allowed errors.
:arg cigar: CIGAR string.
:returns iter: All words in a trie that have Hamming distance of at most
:returns: All words in a trie that have Hamming distance of at most
{distance} to {word}.
"""
if distance < 0:
Expand Down Expand Up @@ -191,66 +188,66 @@ def _levenshtein(path, node, word, distance, cigar):


class Trie(object):
def __init__(self, words=None):
def __init__(self: object, words: list=None) -> None:
"""Initialise the class.
:arg list words: List of words.
:argwords: List of words.
"""
self.root = {}

if words:
for word in words:
self.add(word)

def __contains__(self, word):
def __contains__(self: object, word: str) -> bool:
return '' in _find(self.root, word)

def __iter__(self):
def __iter__(self: object) -> iter:
return _iterate('', self.root, True)

def list(self, unique=True):
def list(self: object, unique: bool=True) -> iter:
return _iterate('', self.root, unique)

def add(self, word, count=1):
def add(self: object, word: str, count: int=1) -> None:
_add(self.root, word, count)

def get(self, word):
def get(self: object, word: str) -> dict:
node = _find(self.root, word)
if '' in node:
return node['']
return None

def remove(self, word, count=1):
def remove(self: object, word: str, count: int=1) -> bool:
return _remove(self.root, word, count)

def has_prefix(self, word):
def has_prefix(self: object, word: str) -> bool:
return _find(self.root, word) != {}

def fill(self, alphabet, length):
def fill(self: object, alphabet: tuple, length: int) -> None:
_fill(self.root, alphabet, length)

def all_hamming_(self, word, distance):
def all_hamming_(self: object, word: str, distance: int) -> iter:
return map(
lambda x: (x[0], distance - x[1], x[2]),
_hamming('', self.root, word, distance, ''))

def all_hamming(self, word, distance):
def all_hamming(self: object, word: str, distance: int) -> iter:
return map(
lambda x: x[0], _hamming('', self.root, word, distance, ''))

def hamming(self, word, distance):
def hamming(self: object, word: str, distance: int) -> str:
try:
return next(self.all_hamming(word, distance))
except StopIteration:
return None

def best_hamming(self, word, distance):
def best_hamming(self: object, word: str, distance: int) -> str:
"""Find the best match with {word} in a trie.
:arg str word: Query word.
:arg int distance: Maximum allowed distance.
:arg word: Query word.
:arg distance: Maximum allowed distance.
:returns str: Best match with {word}.
:returns: Best match with {word}.
"""
if self.get(word):
return word
Expand All @@ -262,28 +259,28 @@ def best_hamming(self, word, distance):

return None

def all_levenshtein_(self, word, distance):
def all_levenshtein_(self: object, word: str, distance: int) -> iter:
return map(
lambda x: (x[0], distance - x[1], x[2]),
_levenshtein('', self.root, word, distance, ''))

def all_levenshtein(self, word, distance):
def all_levenshtein(self: object, word: str, distance: int) -> iter:
return map(
lambda x: x[0], _levenshtein('', self.root, word, distance, ''))

def levenshtein(self, word, distance):
def levenshtein(self: object, word: str, distance) -> str:
try:
return next(self.all_levenshtein(word, distance))
except StopIteration:
return None

def best_levenshtein(self, word, distance):
def best_levenshtein(self: object, word: str, distance: int) -> str:
"""Find the best match with {word} in a trie.
:arg str word: Query word.
:arg int distance: Maximum allowed distance.
:arg word: Query word.
:arg distance: Maximum allowed distance.
:returns str: Best match with {word}.
:returns: Best match with {word}.
"""
if self.get(word):
return word
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = dict-trie
version = 1.0.1
version = 1.0.2
description = Trie implementation for approximate string matching.
long_description = file: README.rst
author = Jeroen F.J. Laros
Expand Down

0 comments on commit 732992d

Please sign in to comment.