Ошибка типа 'NoneType' object is not callable
Необходимо написать декоратор, который загружает файл JSON и делает каждую пару ключ-значение этого файла атрибутом данного класса.
Когда тестирую код, то выбивает ошибку 'NoneType' object is not callable. Возможно, ошибка в написании декоратора и передачи аргументов
def jsonattr(filepath):
def my_decorator(*args):
import json
with open(filepath) as f:
file = json.load(f)
file_items = file.items()
list_of_keys = [x for x, y in file_items]
print(list_of_keys)
foo = list_of_keys[0]
an_int = list_of_keys[1]
this_kata_is_awesome = list_of_keys[2]
return my_decorator
здесь запускаю свой код
@jsonattr("/tmp/myClass.json")
class MyClass:
def __init__(self, foo, an_int, this_kata_is_awesome):
self.foo = foo
self.an_int = an_int
self.this_kata_is_awesome = this_kata_is_awesome
instance = MyClass("hello", 2, None)
и выдаёт ошибку:
input
['foo', 'an_int', 'this_kata_is_awesome']
Traceback (most recent call last):
File "main.py", line 22, in <module>
instance = MyClass("hello", 2, None)
TypeError: 'NoneType' object is not callable
Ответы (1 шт):
Исправление кода из вопроса:
import json
def jsonattr(filepath):
def decorator(cls):
def wrapper(*args):
with open(filepath) as f:
file = json.load(f)
file_items = file.items()
list_of_keys = [x for x, y in file_items]
print(list_of_keys)
foo = list_of_keys[0]
an_int = list_of_keys[1]
this_kata_is_awesome = list_of_keys[2]
return cls(foo, an_int, this_kata_is_awesome, *args)
return wrapper
return decorator
@jsonattr("myClass.json")
class MyClass:
def __init__(self, foo, an_int, this_kata_is_awesome):
self.foo = foo
self.an_int = an_int
self.this_kata_is_awesome = this_kata_is_awesome
def __str__(self):
return f'{self.__class__.__name__}({self.foo!r}, {self.an_int!r}, {self.this_kata_is_awesome!r})'
instance = MyClass("hello", 2, None)
print(instance)
Суть в том, что @jsonattr("myClass.json") - это вызов функции, которая должна вернуть декоратор (подобная ситуация как при создании декоратора с параметрами для функции). Поэтому нужно три слоя функций:
jsonattr- фабрика декораторов, которая при вызове примет параметры самого декоратора и вернет декораторdecorator- декоратор, который вернет функцияjsonattr, вызовется при определении класса, при вызове вернет функцию-обертку или просто измененный классwrapper- обертка для класса, вызовется при создании экземпляра класса, должна вернуть созданный объект класса. По сути эта обертка нужна только если нужно добавить какое-то поведение при создании экземпляра класса, в других случаях в декораторе можно просто менять класс, и его возвращать из декоратора, без использования обертки.
Дальше, если нужно сделать ключи и значения атрибутами класса (буквально по заданию, атрибуты будут прописаны для самого класса):
import json
def jsonattr(filepath):
def decorator(cls):
with open(filepath) as f:
file = json.load(f)
for key, value in file.items():
setattr(cls, key, value)
# Тут wrapper не нужен, т.к. изменили сам класс, его же и возвращаем
return cls
return decorator
@jsonattr("myClass.json")
class MyClass:
def __init__(self, *args):
pass
def __str__(self):
return '{}({})'.format(self.__class__.__name__, ', '.join('{}={!r}'.format(key, getattr(self, key))
for key in filter(lambda key: not key.startswith('_'), dir(self))))
instance = MyClass()
print(instance)
Также можно сделать ключи и значения атрибутами объекта (значения будут недоступны из класса, и независимы в каждом объекте):
import json
from copy import deepcopy
def jsonattr(filepath):
def decorator(cls):
with open(filepath) as f:
file = json.load(f)
def wrapper(*args):
obj = cls(*args)
for key, value in file.items():
# Делаем глубокую копию каждого значения, чтобы значения были независимы в каждом объекте
setattr(obj, key, deepcopy(value))
return obj
return wrapper
return decorator
@jsonattr("myClass.json")
class MyClass:
def __init__(self, *args):
pass
def __str__(self):
return '{}({})'.format(self.__class__.__name__, ', '.join('{}={!r}'.format(key, getattr(self, key))
for key in filter(lambda key: not key.startswith('_'), dir(self))))
instance = MyClass()
print(instance)
Под независимостью я имею в виду, что если из файла загружен такой json: {'foo': []} - то при добавлении значений в список значения будут отображаться только в одном из объектов - в том где мы эти значения добавляем. Если делать атрибуты для класса целиком, то при добавлении значений в такой список они будут видны во всех экземплярах (если значение атрибута полностью перезаписано в каком-то экземпляре - то в нем новые значения отображаться не будет).