Python3 - обработка больших списков: один экземпляр класса или создание нового объекта для каждого элемента? - PullRequest
0 голосов
/ 06 июня 2018

это больше общий вопрос производительности Python.

Я создал простой класс, цель которого:

  1. получить список адресов электронной почты, предоставленных пользователем (в моем случаеЯ использую графический интерфейс tkinter для ввода)
  2. хэшировать каждое письмо, используя алгоритм SHA256
  3. возвращаемое хэшированное значение

Код:

import re
from collections import Iterable
from hashlib import sha256


class HashData():
"""Creates SHA256 sums for iterable prepared for AdWords Customer Match"""

def __init__(self, data):
    if not isinstance(data, Iterable) or isinstance(data, str):
        raise TypeError('data must be iterable and not a string, {} provided'.format(type(data)))

    self.data = data

@staticmethod
def clean(value):
    """
    Prepares string for AdWords' Customer Match requirements:
    - no trailing spaces
    - lowercase
    :param value: str
    :return: str
    """
    if isinstance(value, str):
        return value.strip().lower()
    else:
        return value

def validate_email(self):
    """Validate if self.data is properly formatted email and raise ValueError if not"""

    pattern = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)")

    for mail in self.data:
        mail = HashData.clean(mail)

        if not pattern.match(mail):
            raise ValueError("'{}' doesn't seem to be a valid email!".format(mail))

def encrypt(self):
    """Creates generator for hashing self.data with SHA256 algorithm"""

    for row in self.data:
        value = HashData.clean(row)
        value = value.encode('UTF8')  # hashlib requires encoding before hashing

        yield sha256(value).hexdigest()

Источник:https://github.com/dutkiewicz/adwords-customer-match-hasher/blob/master/hasher/HashData.py

Я беспокоился о производительности, когда пользователь предоставит большой ввод, например, 1 млн. + Строк.Поэтому я подумал, что было бы лучше использовать генератор (self.encrypt ()), который уменьшит использование памяти.Но в то же время я инициализирую объект с полным списком, который может быть огромным: /

Мой вопрос заключается в том, какой подход лучше:

  1. для загрузки ввода в один объект и манипулирования данными?
  2. или прочитать один элемент ввода за раз и каждый раз создавать экземпляр HashData ()?

Если мой вопрос слишком расплывчатый, сообщите мне об этом в комментариях.Это новая проблема, с которой я сталкиваюсь, и я не могу выразить себя наилучшим образом.

1 Ответ

0 голосов
/ 06 июня 2018

Глядя на ваш пример, я имею в виду другой взгляд на код.У меня была бы проблема со строгим доказательством моей точки зрения, но интуитивно HashData не является большой абстракцией.Он имеет дело со списком / генератором внутри класса без предварительного описания операций над элементами списка.Ваш код повторяет for циклы внутри методов, что не является хорошим знаком.

HashData имеет преимущество объединения ваших методов вместе, но, глядя на это с нуля, это в основном цепочка операций в списке строк.Не существует состояния объекта, нескольких переменных, которые можно хранить вместе, или других вещей, которые являются общими для выполнения класса, в отличие от функции.

Мой взгляд на вашу задачу таков:

a) вы можете иметь всего несколько «примитивных» функций, как показано ниже,

import re
from hashlib import sha256

PATTERN = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)")

def is_string(raw_string: str) -> bool:
    return isinstance(raw_string, str)

def clean(raw_string: str) -> str:
    return raw_string.strip().lower()

def is_email(text: str, pattern=PATTERN) -> bool:
    return pattern.match(text)

def encrypt(text: str):
    text = text.encode('UTF8')  # hashlib requires encoding before hashing
    return sha256(text).hexdigest()

b) Вы можете связатьмаленькие функции превращают их в трубу операций, как показано ниже или каким-либо другим способом

email_list = ['gigantic@list1000.com', 'tons@ofemail.org'] * 100

# check type
gen = filter(is_string, email_list)
# cleanup
gen = map(clean, gen)
# filter
gen = filter(is_email, gen)
# encrypt
hash_list = list(map(encrypt, gen))

в) Если вы действительно хотите класс, я бы предложил создать что-то маленькое и управляемое, например, ниже

class Address:
    def __init__(self, raw_string: str):
        if not is_string(raw_string):
            raise TypeError(raw_string)
        text = clean(raw_string)     
        if not is_email(text):   
            raise ValueError(text) 
        self._text = text           

    def email(self):
        return self._text

    def hash(self):
        return encrypt(self._text)

hash_list2 = [Address(s).hash() for s in email_list]

assert hash_list == hash_list2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...