Найти - Пользователи
Полная версия: Последовательные объявления классов
Начало » Python для экспертов » Последовательные объявления классов
1
Evg
Бывает сталкиваюсь с подобными проблемами:

class A:
c = C()

class B:
a = A()

class C:
b = B()
т.е как не переставляй объявления классов работать не будет)

Как нибудь можно заставить на питоне работать такое?
И как вообще такое решается?
regall
А можно практический пример?
А то я, например, никогда с такими потребностями не сталкивался, тут, по-моему, попахивает идеологической ошибкой.
Evg
Пример будет тогда из джанги) изобретаю такой вот велосипед под названием AggregateField.
в кратце это денормализованное поле которое автоматически следит за количеством связанных объектов в отношении 1:М.

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save
from django.db.models.signals import post_delete

class AggregateCountField(models.PositiveIntegerField):
...
def __init__(self, agg_model, fk, *args, **kwargs):
self.agg_model = ContentType.objects.get(name = agg_model).model_class()
#далее идет поптыка привязатеь сигналы к модели Comment которая еще не определена
post_save.connect(self.recalc_count, sender=self.agg_model)
post_delete.connect(self.recalc_count, sender=self.agg_model)
# и поэтому сигналы не привязываются
...

class Text(models.Model):
comments_count = AggregateCountField('Comment','text')

class Comment(models.Model):
text = models.ForeignKey(Text)
Андрей Светлов
Решается, конечно.

Не совсем понятно, где именно видно, что ‘модель Comment еще не определена’
Простите, я не спец по Джанге - так, поглядывал искоса…

Дальше идет отсебятина по решению “в лоб”.

Самый простой вариант: завернуть comments_count через property.
Будет вызываться при каждом обращении.
Чтобы такого не было, пишем

Делай раз:
class BlahBlah(...):

_stored_comments_count = None

def _get_comments_count(self):
if self._stored_comments_count is None:
self._stored_comments_count = AggregateCountField('Comment','text')
return self._stored_comments_count

comments_count = property(_get_comments_count)
Если напрягает повторять это от раза к разу - делай два:
class const(object):
"""
Constant property.
Always return value calculated first time.
Good for immputable types.
"""

class _Value(AddOn):
def __init__(self, obj, func):
self.value = func(obj)

def __init__(self, func):
self._func = func
self.__doc__ = func.__doc__

def __get__(self, instance, owner):
assert instance is not None
return self._Value(instance, self._func).value

class BlahBlah(...):
@const
def comments_count(self):
return AggregateCountField('Comment','text')
Ой, это я немного перескочил:
начал использовать AddOns, который имеет возможность сохранять посчитанное в любую переменную, даже не имеющую своего __dict__.
Для class attribute должно хватить getattr/setattr с обращением через owner и name = ‘_my_cool_and_hidden_class_varibale_name_for_my_const_’
Взял куссочек своего кода - а он AddOns уже использует.

В любом случае стоит сделать шаг три:
Прочитать очень старую статью на python.org: http://www.python.org/download/releases/2.2/descrintro/
Она дает нужное понимание - как они работает. И как не работают. И почему работают не так, как ожидалось.

Дескрипторы и метаклассы - одна из основ питоновской магии.
Плюс, конечно, осознание (на этом слове вспоминается Олег Дивов с “У Билли есть Хреновина”) того, что любая вещь в нем - объект.
Со своими методами, атрибутами (изменяемыми или константными) и проч.
Функция - это объект, как module, frame и code object.
Резюмируя - очень интересный мир.
Функция dir, модули inspect и dis с чудесной функцией dis в нем - путь попасть в понимание того, как _именно_ питон функционирует.

Надеюсь, не окончательно запутал.

Кстати, j2a:
сам тоже никогда еще не подменял базовый класс - но знал, что можно. И согласен, что так делать обычно не стоит.
Но если мне потребуется написать хитрую диагностическую тулзу - возможно я опять об этом вспомню (и применю в тему).
Питоновская Магия (по словам Сергея Щетинина, которого люблю и уважаю) делится на три части:
- белая. Ты используешь всю мощь языка для тех целей, которые предполагались.
- серая. Решения сомнительные, но текущая реализация питона их позволяет.
Так почему бы и нет - если без серой магии не обойтись никак.
При условии что она - элегантная и дает красивые решения.
- черная. Выглядит тупо, страшно и непонятно.
Такой код нужно искоренять. Читабельность - в первую очередь.
Если ты, конечно, хороший питонщик - иначе читабельность воспринимается по разному.
Кстати, pkg_resources - просто ужасен. Хоть и как-то работает.
Evg
Андрей Светлов
Не совсем понятно, где именно видно, что ‘модель Comment еще не определена’
Когда до описания класса Text доходит там определяется поле
comments_count = AggregateCountField('Comment','text')
после этого вызывается конструктор AggregateCountField в котором идет попытка достать класс Comment:
self.agg_model = ContentType.objects.get(name = agg_model).model_class()
но так как выполнение еще не дошло до Comment там NoneType получается.

Андрей Светлов
Самый простой вариант: завернуть comments_count через property.
В простом случае с классами может такое и прошло бы но в джанго что-то не проходит - там на самом деле при объявлении каждой модели ( например class Text(models.Model)) вызывается еще какой то мета класс который там много чего колдует и ему описания полей нужны именно в таком формате (comments_count = AggregateCountField('Comment','text')) иначе он просто не воспримет это как описание поля.

С метаклассами я вообще не очень еще прочувствовал, например не понимаю зачем в джанге для всех моделей есть метакласс ModelBase и почему всю логику что он делает например нельзя было вынести в обычный базовый класс Model и в чем такое преимущество его использования.

Вот есть например при определении мета класса метод __new__, в чем такое его преимущество в использовании, чего например нельзя сделать в обычном конструкторе __init__? Вообще хорошо бы увидеть какой то пример на котором показано использование метаклассов и явно видно преимущество, над тем что нельзя сделать тоже другими способами или это получается например явно сложнее.
pasaranax
А как на счет стиля С++?
class A: pass
class B: pass
class C: pass

class A:
c = C()

class B:
a = A()

class C:
b = B()
Evg
pasaranax
А как на счет стиля С++?
class A: pass
class B: pass
class C: pass

class A:
c = C()

class B:
a = A()

class C:
b = B()
это я вчера тоже попробовал но опять же джанго не захавал, а для примера может и прокатит)
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB