Как создать класс с указанными характеристиками? - PullRequest
0 голосов
/ 24 января 2020

Что я хочу сделать, это создать класс, который я назову Cluster, который я могу использовать для хранения представлений многомерных точек данных. Как куча людей высот, весов и возрастов. Я хочу сделать так, чтобы при создании экземпляра Cluster он возвращал пустой словарь. Затем я хочу иметь возможность добавлять пары ключ-значение. Я хочу, чтобы каждое значение в паре с каждым ключом, чтобы только было списком. Я хочу, чтобы каждый список содержал только один тип данных.

Однако они не обязательно должны быть одинакового типа данных для каждого списка. Я имею в виду, что один список может состоять только из целых чисел, а другой список содержит только строки. Я даже допускаю список кортежей, списков или словарей. Им даже не обязательно иметь один и тот же тип данных для каждого значения. Но общий список должен содержать все словари, кортежи и т. Д. c.

Пример Cluster:

{'Hobbies': [(3, 's', {'name': 'Sam'}), (4, 5), (), (True)], 'Weights': [1, 34, 3, 90]}

Обратите внимание, что указанная выше пара значений 'Хобби' представляет собой список из только кортежей и пары значений 'Weights' список из только целых чисел Это то, что я имею в виду под тем, что я пытаюсь сказать выше.

Я создал функцию, которая генерирует то, что Cluster, полное значений, должно выглядеть , но я хочу, чтобы оно было класс. Это дает вам то, что я хочу, но я хочу сделать class, а не функцию, которая генерирует это. Другие методы, такие как получение длины Cluster Я могу понять, я просто хочу, чтобы все, что я сказал выше, было правдой.

from random import randint


def create_cluster(a, b, c, *d):
    cluster = {}
    for key in d:
        point = []
        for integer in range(0, a):
            point.append(randint(b, c))
        cluster[key] = point
    return cluster


print(create_cluster(4, 2, 9, 'Height', 'Weight', 'Age'))

Вывод:

{'Height': [5, 3, 3, 6], 'Weight': [7, 3, 5, 7], 'Age': [9, 5, 3, 6]}

Ответы [ 2 ]

0 голосов
/ 15 февраля 2020

Если я понимаю, что вам нужно, мы действительно можем создать объект dict, который будет проверять тип для вас! Нет необходимости импорта.

Нам просто нужно создать подкласс для класса dict и переопределить функции для добавления элементов в словарь, чтобы мы могли проверять тип, прежде чем разрешать какие-либо добавления.

Вот код ниже.

class ClusterDict(dict):
    key_type = str  # we want all of our keys to be str type
    value_type = list  # same for our value as a list

    # lets override the function for dictionary assignment so we can type check before we add to our dictionary
    def __setitem__(self, key, value):
        self.type_check_and_add(key, value)

    # Lets override update as well
    def update(self, **kwargs) -> None:
        for key, value in kwargs.items():
            self.type_check_and_add(key, value)

    def type_check_and_add(self, key: str, value: list) -> None:
        assert type(key) == self.key_type, f"Expected {self.key_type} for key, got {type(key)} instead."
        if type(value) is not list:
            value = [value]  # Make it a list if its isn't, remove this if you know it will always come as a list, even if its a single entry, like [5], or [(2, 6)]
        assert type(value) == self.value_type, f"Expected {self.value_type} for key, got {type(value)} instead."
        val_type = type(value[0])  # type of first item in list, lets make sure they all match this one
        for val in value:
            assert type(val) == val_type, f"Items do not have matching types for key {key}."
        super().__setitem__(key, value)  # this then passes assignment as usual to the base class dict

Пройдем несколько тестов сейчас!

if __name__ == '__main__':
    clust = ClusterDict()
    input_data = {
        'Height': [4, 3, 5],
        'Age': [(4, 9), (4, 6, 8)]
    }
    clust.update(**input_data)
    print(clust)
{'Height': [4, 3, 5], 'Age': [(4, 9), (4, 6, 8)]}

Пока все выглядит хорошо, мы можем добавлять целые словари, используя обновление как обычно

    input_data = {'Weights': [1, 34, 3, 90]}
    clust.update(**input_data)
    print(clust)
{'Height': [4, 3, 5], 'Age': [(4, 9), (4, 6, 8)], 'Weights': [1, 34, 3, 90]}

Или только одна запись за раз.

Давайте попробуем обычным способом, dict [ключ] = значение

    clust['Hobbies'] = [(3, 's', {'name': 'Sam'}), (4, 5), (), (True,)]
    print(clust)
{'Height': [4, 3, 5], 'Age': [(4, 9), (4, 6, 8)], 'Weights': [1, 34, 3, 90], 'Hobbies': [(3, 's', {'name': 'Sam'}), (4, 5), (), (True,)]}

Хорошо выглядит. Но теперь давайте попробуем добавить список с типами, которые не соответствуют.

    clust['My Favorite Number'] = [7, '7', (1, 1, 1, 1, 1, 1, 1)]
    print(clust)
    assert type(val) == val_type, f"Items do not have matching types for key {key}."
AssertionError: Items do not have matching types for key My Favorite Number.

, который выдает ошибку AssertionError и не позволяет нам делать это, как мы хотели.

Хотя бонус Поскольку мы подклассифицируем dict, мы получаем все его методы бесплатно!

>>>len(clust)
4

for k, v in clust.items():
...     print(k, v)
...     
Height [4, 3, 5]
Age [(4, 9), (4, 6, 8)]
Weights [1, 34, 3, 90]
Hobbies [(3, 's', {'name': 'Sam'}), (4, 5), (), (True,)]

Надеюсь, это то, что вы искали, и это поможет!

0 голосов
/ 24 января 2020

Как упоминает Code-Apprentice, не существует механизма, с помощью которого python обеспечивает проверку типа в том смысле, что он остановит вас от компиляции и запуска кода. Начиная с версии 3.5, однако, Python позволяет вам осуществлять мягкую проверку типов - вы можете прочитать об этом здесь . Это означает, что Python теперь также предлагает вам способ проверить, передает ли ваш код недопустимые значения в функции / классы / переменные и т. Д. c.

Вот как это работает:

from typing import Dict, List, Any, Tuple, Union
from random import randint

class Cluster:
    def __init__(self):
        self.data: Dict[str, List[Any]] = {}

    def create_cluster(self, a, b, c, *d):
        for key in d:
            point = []
            for integer in range(0, a):
                point.append(randint(b, c))
            self.data[key] = point

clust = Cluster()
clust.create_cluster(4, 2, 9, 'Height', 'Weight', 'Age')
print(clust.data)

Редактировать: специально для вашего примера вы хотели бы что-то вроде:

self.data: Dict[str, List[Union[Tuple, int]] = {}

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

self.data: Dict[str, List[Union[Tuple[Union[int, str]], int]] = {}

Далее, вам нужно установить mypy . Mypy должен вызываться отдельно от терминала в вашем скрипте, но он будет go через код и выполнять проверку типа - то есть он будет предупреждать вас о любых случаях, когда вы объявили недопустимые переменные в вашем классе.

Кроме того, вы можете выполнить серию внутрикодовых перехватов, включающих:

if type(data['Weight'][0]) != whatever:
    raise("You're putting the wrong type of variable in")

Но mypy кажется лучшим решением в долгосрочной перспективе. Это избавит вас от необходимости ждать выполнения, запуска и сбоя кода в предсказуемом месте.

Обратная связь и исправления приветствуются.

...