В python есть ли способ автоматического запуска функций сразу после определения класса? - PullRequest
2 голосов
/ 14 января 2020

Я занимаюсь разработкой класса. Данные на уровне класса, которые ему понадобятся, будут относительно сложными. Чтобы сохранить типизацию и минимизировать ошибки, я хотел бы определить большую часть этих данных с помощью функций. Кроме того, я хотел бы сделать эти данные доступными для пользователя, даже если они не готовы создать экземпляр класса. Итак, мне интересно, есть ли способ заставить эти функции запускаться автоматически, как только класс определен? Например, я хочу что-то вроде

import numpy as np    

def class foo:

        @this_should_run_before_instantiation
        def define_bar:
            bar = np.range(100)

       @this_should_also_run_before_init
       def define_something_really complicated:
            bleh = # something that is too complicated for a single line or lambda

        def __init__(self):
            # etc.

Так что пользователь может сделать что-то вроде

>>>from foopackage import foo
>>>foo.bar
array([ 0,  1,  2,  3,  4,  5,  ... #etc.
>>>foo.bleh
< whatever bleh is ... >

Ответы [ 5 ]

6 голосов
/ 14 января 2020

Разумный / простой способ справиться с этим - для вас, как автора класса, сделать это явно . Так что-то вроде:

class Foo:
    @classmethod # don't have to be but will likely benefit from being classmethods...
    def _define_something_really_complicated(cls):
        ...
    @classmethod
    def _define_something_else_really_complicated(cls):
        ...
    @classmethod
    def _build(cls):
        cls._define_something_really_complicated()
        cls._define_something_else_really_complicated()
        ...
    def __init__(self):
        ...

Foo._build() # where your module is defined.

Тогда всякий раз, когда потребитель делает import foo; my_foo_instance = foo.Foo(), он уже будет построен.

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

2 голосов
/ 14 января 2020

Атрибуты класса инициализируются при определении класса. Никакая функция не требуется.

import numpy as np    

class Foo:
    bar = np.range(100)
    def __init__(self):
        # etc.

Если вам нужна функция, которую можно вызывать после импорта, но перед тем, как создать экземпляр класса, вы можете определить функцию класса.

class Foo:
    @classmethod
    def initialize_bar(cls):
        cls.bar = np.range(100)

Тогда ...

>>> from foopackage import Foo
>>> Foo.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute 'bar'
>>> Foo.initialize_bar()
>>> Foo.bar
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
       34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
       51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
       68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
       85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99])
1 голос
/ 14 января 2020

Возможно это (вариант 1)

def complex_data_calc():
    return range(42)

class Foo:
    bar = compex_data_calc()

    def __init__(self):
        pass

или это (вариант 2)

class Foo:
    @staticmethod
    def bar():
        return range(42)

    def __init__(self):
        pass

и вызовите с

from foopackage import Foo
Foo.bar    # variant 1
Foo.bar()  # variant 2
1 голос
/ 14 января 2020

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

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

# module1.py
def foo():
  return bar

A = 100
print('You just imported module1')

Тогда вы можете import module1 и увидеть напечатанное сообщение, а foo.A равным 100.

Выполнение кода с боковой эффекты во время импорта обычно не самая блестящая идея; убедитесь, что вы понимаете, что вы делаете и почему. (В противном случае не делайте этого и предоставьте функцию инициализации.)

1 голос
/ 14 января 2020

Вы можете попробовать:

#file Foo.py

import numpy as np

bar = np.range(100)

class foo:
    pass

Тогда

# file main.py
import Foo

b = Foo.bar
foo_instance = Foo.foo()
...