1. ホーム
  2. django

[解決済み] `SyntaxError: nonlocal 'topics_with_log_tag' のバインディングが見つからない` けど、バインディングはされている

2022-02-27 18:11:32

質問

指定した'log'タグを持つトピックを取得したいのですが。

ネストされた関数内 get_topics_with_log_tag 変数 topics_with_log_tag 非局所的である。

def log(request):
    """Show all topics and entries with  log tags"""

    topics = Topic.objects.all()
    #select entries with log tag
    def get_topics_with_log_tag(topics):
        nonlocal topics_with_log_tag
        topics_with_log_tag = []
        for topic in topics:
            for entry in topic.entry_set.all():
                if "#log" in entry.tags:
                    topics_with_log_tag.append(topic)

    get_topics_with_log_tag(topics)

SyntaxError をスローします。

SyntaxError: no binding for nonlocal 'topics_with_log_tag' found

実際、私はそれをバインドしました topics_with_log_tag = []

上記のコードを冗長に書き直すと、次のようになります。

topics = Topic.objects.all()
#select entries with log tag
def get_topics_with_log_tag(topics):
    # nonlocal topics_with_log_tag
    topics_with_log_tag = []
    for topic in topics:
        for entry in topic.entry_set.all():
            if "#log" in entry.tags:
                topics_with_log_tag.append(topic)
    return topics_with_log_tag

topics_with_log_tag = get_topics_with_log_tag(topics)

の使い方のどこが問題なのでしょうか? nonlocal ?

エラーが見つかりました。

Willem Van Onsemは、ネストされたforループの代わりに、データベースレベルのフィルタを導入しています。

models.py

 class Topic(models.Model):
    """A topic the user is learning about."""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User)

    def __str__(self):
        """Return a string representation of the model."""
        return self.text

class Entry(models.Model):
    """Something specific learned about a topic"""
    topic = models.ForeignKey(Topic)
    title = models.CharField(max_length=200)
    text = models.TextField()
    tags = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

解決方法は?

どのように nonlocal 動作

を使用する場合 nonlocal これは、Pythonが関数の開始時に、1つ上のスコープから同じ名前の変数を探すことを意味します(さらにその先も)。しかし、ここではそのような変数は定義されていません。1つ上の階層にある変数を定義することで解決できます。

def log(request):
    """Show all topics and entries with  log tags"""

    topics = Topic.objects.all()
    #select entries with log tag
    topics_with_log_tag = []
    def get_topics_with_log_tag(topics):
        nonlocal topics_with_log_tag
        topics_with_log_tag = []
        for topic in topics:
            for entry in topic.entry_set.all():
                if "#log" in entry.tags:
                    topics_with_log_tag.append(topic)

    get_topics_with_log_tag(topics)

を使用することができます。 global この場合、変数を宣言する必要はありませんが(この場合は上位で宣言します)、実はこれもアンチパターンです。

Django ORMで効率的なデータベースクエリ

しかし、ここでフィルタリングを実行する方法は、通常、非常に非効率的です。 Topic を取得し、次に各トピックに対して追加のクエリを実行し、すべての Entry を指定し、次に各 Entry を取得し、すべての Tag であるかどうかを調べ、そのタグが #log . ここで、10個のトピックがあり、1個のトピックに10個のエントリーがあり、1個のエントリーに5個のタグがあるとします。その結果、データベースレベルで500以上のクエリーを実行することになります。このようなフィルタを構築することができます。

topics_with_log_tag = Topics.objects.filter(entry__tags__contains='#log').distinct()

またはもっと読みやすい(複数行の式を可能にするために括弧が使用されています)。

topics_with_log_tag = (Topics.objects
                             .filter(entry__tags__contains='#log')
                             .distinct())

上記は、(あなたのコードと同じように)トピックを含むことに注意してください。 tags 例えば '#logarithm' . これは、ある部分文字列を含むかどうかだけをチェックするものです。これを防ぐには、より高度なフィルタリングを行うか、(エンドマーカーを使った)より優れたタグ表現が必要になります。

例えば、すべてのトピックがコンマで終わっている場合(例えば '#foo,#bar,' をクエリすることができます。 '#log,' .

また、正規表現を用いて、新しいハッシュ文字や、文字列の終わりをチェックすることもできます。