Внутренние объекты класса не сбрасываются после создания пользовательского класса в Python 3.6 - PullRequest
0 голосов
/ 26 апреля 2018

Я использую Python 3.6.4 и создал собственный класс, где я делаю вычисления и изменяю одну из внутренних переменных класса. Я заметил, что он всегда работает правильно, когда я запускаю алгоритм (например, создание экземпляра класса и т. Д.) И всегда провал во второй раз. Даже если это один и тот же код строки, повторяемый дважды подряд. Мне удалось воспроизвести ошибку в более простой форме.

Почему lr_space, который изменяется в первом объекте, распространяется на 2-й экземплярный объект?

class testing(object):
    def __init__(self, 
                n_iter=5,
                n_space=10,
                model_type="logistic",
                lr_space={
                    "C":(1e-6, 1.0),
                    "penalty":["l1", "l2"],
                },
                lr_kws=dict(max_iter=10000, solver="liblinear"),
                ):
        self.n_iter = n_iter
        self.n_space = n_space
        # Logistic Regression
        self.lr_space = lr_space
        self.lr_kws = lr_kws
        print("", self, self.lr_space,"", sep="\n\t")
        self.model_type = model_type.lower()
        self.models = self._test_function()

    def _test_function(self):
        """
        Internal: Label models
        Need to extend this for using different hyperparameters
        """
        models = list()
        self.param_index = OrderedDict()

        # Indexing for hyperparameters and models
        a = np.ones(self.n_iter*2)
        b = np.arange(a.size)
        if self.model_type == "logistic":
            self.lr_space["C"] = np.linspace(*self.lr_space["C"], self.n_space)


        return models
print("=====Instantiating and running `instance_1`=====")
instance_1 = testing()
print("=====Instantiating and running `instance_2`=====")
instance_2 = testing()

Выход:

=====Instantiating and running `instance_1`=====

    <__main__.testing object at 0x136154400>
    {'C': (1e-06, 1.0), 'penalty': ['l1', 'l2']}

=====Instantiating and running `instance_2`=====

    <__main__.testing object at 0x127649390>
    {'C': array([  1.00000000e-06,   1.11112000e-01,   2.22223000e-01,
         3.33334000e-01,   4.44445000e-01,   5.55556000e-01,
         6.66667000e-01,   7.77778000e-01,   8.88889000e-01,
         1.00000000e+00]), 'penalty': ['l1', 'l2']}

Ошибка:
-------------------------------------------------- ------------------------- TypeError Traceback (последний вызов был последним) в () 38 instance_1 = testing () 39 печатных ("===== Создание и запуск instance_2 =====") ---> 40 instance_2 = testing ()

<ipython-input-342-24f241984973> in __init__(self, n_iter, n_space, model_type, lr_space, lr_kws)
     17         print("", self, self.lr_space,"", sep="\n\t")
     18         self.model_type = model_type.lower()
---> 19         self.models = self._test_function()
     20 
     21     def _test_function(self):

<ipython-input-342-24f241984973> in _test_function(self)
     31         b = np.arange(a.size)
     32         if self.model_type == "logistic":
---> 33             self.lr_space["C"] = np.linspace(*self.lr_space["C"], self.n_space)
     34 
     35 

TypeError: linspace() takes from 2 to 6 positional arguments but 11 were given

Ответы [ 2 ]

0 голосов
/ 26 апреля 2018

В итоге я использовал deepcopy из встроенного copy:

import copy
class testing(object):
    def __init__(self, 
                n_iter=5,
                n_space=10,
                model_type="logistic",
                lr_space={
                    "C":(1e-6, 1.0),
                    "penalty":["l1", "l2"],
                },
                lr_kws=dict(max_iter=10000, solver="liblinear"),
                ):
        self.n_iter = n_iter
        self.n_space = n_space
        # Logistic Regression
        self.lr_space = copy.deepcopy(lr_space)
        self.lr_kws = lr_kws
        print("", self, self.lr_space,"", sep="\n\t")
        self.model_type = model_type.lower()
        self.models = self._test_function()

    def _test_function(self):
        """
        Internal: Label models
        Need to extend this for using different hyperparameters
        """
        models = list()
        self.param_index = OrderedDict()

        # Indexing for hyperparameters and models
        a = np.ones(self.n_iter*2)
        b = np.arange(a.size)
        if self.model_type == "logistic":
            self.lr_space["C"] = np.linspace(*self.lr_space["C"], self.n_space)


        return models
0 голосов
/ 26 апреля 2018

Решение:

Если вы назначите значение по умолчанию lr_space внутри функции init, вместо этого оно будет работать:

 from collections import OrderedDict
 import numpy as np

 class testing(object):
     def __init__(self, 
                 n_iter=5,
                 n_space=10,
                 model_type="logistic",
                 lr_space=None,
                 lr_kws=dict(max_iter=10000, solver="liblinear"),
                 ):
         if lr_space is None:
           lr_space = {
                     "C":(1e-6, 1.0),
                     "penalty":["l1", "l2"],
           }
         self.n_iter = n_iter
         self.n_space = n_space
         # Logistic Regression
         self.lr_space = lr_space
         self.lr_kws = lr_kws
         print("", self, self.lr_space,"", sep="\n\t")
         self.model_type = model_type.lower()
         self.models = self._test_function()

     def _test_function(self):
         """
         Internal: Label models
         Need to extend this for using different hyperparameters
         """
         models = list()
         self.param_index = OrderedDict()

         # Indexing for hyperparameters and models
         a = np.ones(self.n_iter*2)
         b = np.arange(a.size)
         if self.model_type == "logistic":
             self.lr_space["C"] = np.linspace(*self.lr_space["C"], self.n_space)


         return models
 print("=====Instantiating and running `instance_1`=====")
 instance_1 = testing()
 print("=====Instantiating and running `instance_2`=====")
 instance_2 = testing()

Почему:

Когда вы присваиваете значения по умолчанию для аргументов в def __init__(...), им присваивается до , новый экземпляр существует. Это не имеет значения при использовании простых неизменяемых значений, таких как 5 и "logistic", но если вы используете dict, вы создаете объект вне экземпляра, а затем назначаете его для ссылки в вызове __init__ .

Это опасный анти-паттерн, который вам следует избегать. Вы можете прочитать больше об этом здесь: Использование изменяемого значения по умолчанию в качестве аргумента

Когда вы создаете новые экземпляры, ссылка снова назначается, но она все еще ссылается на тот же объект. Ваш код выше эквивалентен:

default_dict = {
    "C":(1e-6, 1.0),
    "penalty":["l1", "l2"],
}

class testing(object):
     def __init__(self, 
            n_iter=5,
            n_space=10,
            model_type="logistic",
            lr_space=default_dict,
            lr_kws=dict(max_iter=10000, solver="liblinear"),
            ):
...