Python Forum

Full Version: First Python Project
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello! My first Python project is called python-utils. You can find the full project on GitHub.

Features
  • Decorators - Dynamically alter the functionality of your functions.
  • String Functions - Functions for creating or manipulating strings.
  • List Functions - Functions for extracting, displaying, joining, or manipulating lists.
  • Async Functions - Useful set of Async functions for multi-threading.
  • Translator - Quickly translate text with multiple language options.

I am looking for constructive criticism, optimization suggestions, additions, or just ideas. All replies are appreciated. :)

AsyncUtils

# Executes the specified function asynchronously.
# Example: ExecuteFunctionAsync(print, "Hello World", delay=5)
class ExecuteFunctionAsync(threading.Thread):
    def __init__(self, func, *args, delay: int = 0):
        super(ExecuteFunctionAsync, self).__init__()
        self.func = func
        self.delay = delay
        self.args = args

    def run(self):
        if self.delay > 0:
            time.sleep(self.delay)
        self.func(*self.args)
Decorators

# Time decorator.
# Add above any function to print execution time.
def time(func):
    import time

    @wraps(func)
    def wrapper(*args, **kwargs):
        before = time.time()
        result = func(*args, **kwargs)
        after = time.time() - before
        print("{} ran in: {} sec".format(func.__name__, after))
        return result
    return wrapper


# Asynchronous decorator.
# Add above any function to execute async.
def async(func):
    import threading

    @wraps(func)
    def wrapper(*args, **kwargs):
        thread = threading.Thread(target=func, args=args, kwargs=kwargs)
        thread.start()
        return func
    return wrapper
LangUtils

from enum import Enum
from bs4 import BeautifulSoup
import requests


# A translation function.
# Example: translate("Hello, how are you!", Language.SPANISH) returns "¿Hola como estas?"


def translate(text: str, language: Enum):
    url = 'https://www.google.com/search?q=' + text + '+in+' + language.name.lower() + '&oq=' + text\
          + '+in+' + language.name.lower() + '&aqs=chrome.0.0l6.3047j1j7&sourceid=chrome&ie=UTF-8'

    src = requests.get(url)
    plain = src.text
    readable = BeautifulSoup(plain, "html.parser")
    results = []

    for link in readable.findAll("span", {"class": "nobr"}):
        results.append(link.string)
    return results[1]


class Language(Enum):
    SPANISH = 1
    FRENCH = 2
    GERMAN = 3
    DANISH = 4
    ARABIC = 5
    CHINESE = 6
    JAPANESE = 7
    KOREAN = 8
ListUtils

# Connects two lists.
# Example: connect(['1', '2'], ['3', '4']) returns ['1', '2', '3', '4']
def connect(first: list, second: list):
    return first + second


# Removes all empty values from a list.
# Example: trim(['1', '2', '3', '']) returns ['1', '2', '3']
def trim(array: list):
    for i in range(array.__len__()):
        try:
            value = array[i]
            if value is '' or value is None:
                array.__delitem__(i)
        except IndexError:
            pass
    return array


# Separates each value in a list with a symbol.
# Example: join(['1', '2', '3'], ':') returns "1:2:3"
def join(array: list, symbol: str):
    text = ""
    length = array.__len__()
    for i in range(length):
        if i == (length - 1):
            text += array[i]
        else:
            text += array[i] + symbol
    return text


# Capitalize all string values in a list
# Example: capitalize(['a', 'b', 'c']) returns ['A', 'B', 'C']
def capitalize(array: list, first: bool = False):
    if first:
        return list(map(str.capitalize, array))
    else:
        return list(map(str.upper, array))


# Lowercase all string values in a list
# Example: lowercase(['A', 'B', 'C']) returns ['a', 'b', 'c']
def lowercase(array: list, first: bool = False):
    if first:
        vs = []
        for i in range(array.__len__()):
            vs.append(array[i][0].lower() + array[i][1:])
        return vs
    else:
        return list(map(str.lower, array))


# Returns a new list containing values with the specified data type.
# Example: extract(["Hello, World!", 1, 2, 3], int) returns [1, 2, 3]
def extract(array: list, data: type):
    elements = []
    for value in array:
        if type(value) == data:
            elements.append(value)
    return elements
Just a few random thoughts
  • some of these implement functionality that has idiomatic ways to do in python or implement naive approach to functionality that is already available:
    - reverse: why would I wrap slicing in a function and introduce uncertainty what else [possibly] is going behind the curtains of my reverse function?
    - join: do you know/try str.join() method, e.g. ':'.join(['1', '2', '3']) before writing your own function?
    - repeat: do you know/try '#' * 5 before writing your own function?
    - add_commas - did you check string formatting - https://docs.python.org/3/library/string...ing-syntax? why would you cast a number to str just for representation purpose?
    - connect: do you know/check list.extend() method, e.g. list1.extend(list2)?
    - trim: why I would not use [item for item in my_list if item]. Or at least you should implement it this way

  • some of these are inefficient, e.g. all the remove functions. Just an example

    txt = '12jljljl2ddf56gt..jskaj;dlJA;SLKDj;sdjA;SKJD;ajsd;KJSD;IJ`239183`   DOJasjds   q098i-JASDkljsdklJA;SDLJKa;slkdjA;LSKDJ J`230-9E8-098E03SJD;lajsd;askljd;KJASD;Lkjasd;lJAK;SDKLa'
    import string
    import timeit
    
    def remove_numbers(text):
        updated = ""
        for chars in text:
            if not chars.isdigit():
                updated += chars
        return updated
    
    def buran_remove_numbers(text):
        for c in string.digits:
            text.replace(c, '')
        return text
    
    elapsed = timeit.timeit('remove_numbers(txt)', number=50000, setup='from __main__ import remove_numbers, txt')
    print 'remove_numbers took {:.3f} sec'.format(elapsed)
    
    elapsed = timeit.timeit('buran_remove_numbers(txt)', number=50000, setup='from __main__ import buran_remove_numbers, txt')
    print 'buran_remove numbers took {:.3f} sec'.format(elapsed) 
    Output:
    remove_numbers took 1.033 sec buran_remove numbers took 0.174 sec
    the longer the string the bigger the time gap would be

  • never use for i in range(array.__len__()): to iterate over iterables like list. check https://python-forum.io/Thread-Basic-Nev...n-sequence

  • it is prefered to use isinstance() instead of type()

Thanks a lot for the response! I wasn't aware that many of the functions / methods you suggested even existed while making python-utils. I've made some edits to my code. Please let me know what you think:

# Adds commas to a number.
# Example: add_commas(1000) returns "1,000"
def add_commas(number: int):
    return '{:,}'.format(number)

# This function creates a string with a repeating argument.
# Example: repeat("#", 5") returns "#####"
def repeat(args: str, amount: int):
    return args * amount

# Removes all empty values from a list.
# Example: trim(['1', '2', '3', '']) returns ['1', '2', '3']
def trim(array: list):
    return [item for item in array if item]

# Lowercase all string values in a list
# Example: lowercase(['A', 'B', 'C']) returns ['a', 'b', 'c']
def lowercase(array: list, first: bool = False):
    if first:
        vs = []
        for i, item in enumerate(array):
            vs.append(array[i][0].lower() + array[i][1:])
        return vs
    else:
        return list(map(str.lower, array))

# Removes all numbers from the string.
def remove_numbers(text: str):
    for c in string.digits:
        text.replace(c, '')
    return text


# Removes all letters from the string.
def remove_letters(text: str):
    for c in string.ascii_letters[:26]:
        text.replace(c, '')
    return text

# Returns a new list containing values with the specified data type.
# Example: extract(["Hello, World!", 1, 2, 3], int) returns [1, 2, 3]
def extract(array: list, data: type):
    return [item for item in array if isinstance(item, data)]
Maybe I was not clear, but my comments were that you don't need many of these (and certainly you don't need all from my first bullet), not that you should implement them differently (this does not apply to the inefficient ones).
Let's take for example add_commas. You would keep the number and use format where you want to print. In addition f-strings that are available in 3.6+ allow for calculations. Bottom line - generally you don't want to convert the number to string just for representation (i.e. with commas)

num_items = 2500
price = 10
print('{:,d} items @ EUR {:.2f} per item --> Total EUR {:,.2F}'.format(num_items, price,num_items*price))
print(f'{num_items:,d} items @ EUR {price:.2f} per item --> Total EUR {num_items*price:,.2f}')
Output:
2,500 items @ EUR 10.00 per item --> Total EUR 25,000.00 2,500 items @ EUR 10.00 per item --> Total EUR 25,000.00
or repeat - one would always use string multiplications (if not for something else, because it is shorter) than using repeat function.