簡體   English   中英

Python:如何從類變量訪問class .__ dict__?

[英]python: how to access class.__dict__ from class variable?

我需要定義一個名為“ class”的類變量。 我想直接在類名稱空間而不是在類方法中執行此操作。 顯然,我不能直接說:

class MyClass(object):
    a = 1
    b = 2
    class = 3

所以,我想做類似的事情:

class MyClass(object):
    a = 1
    b = 2
    self.__dict__["class"] = 3

其中“ self ”應替換為對該類的引用。 那么,如何從類名稱空間引用一個類呢?


注意:這個問題看似人為,但這是出於實際目標。

實際上, MyClass是Django REST框架的序列化程序,我需要在其上定義“類”字段,因為該REST端點必須遵循某種協議。

有一個為Serializers定義的元類,它在類創建時調用__new__() ,而__new__()聚集在類上定義的所有字段,並使用它們填充字段注冊表。 因此,我必須在創建class之前定義變量class 另請參閱: Django REST Framework:如何使字段的詳細名稱與其字段名稱不同?

您可以這樣做:

class MyClass(object):
    a = 1
    b = 2
    vars()['class'] = 3

但是,由於class是保留關鍵字,因此您必須使用getattrsetattr訪問變量,以便class仍然是字符串。

>>> m = MyClass()
>>> getattr(m, 'class')
3

您可以從type創建您的類,並將屬性class添加到類字典中:

>>> MyClass = type('MyClass', (), {'class': 3, 'a':1, 'b':2})
>>> getattr(MyClass, 'class')
3

您不能直接使用點引用訪問名稱class ,而需要使用getattr

>>> MyClass.class
  File "<stdin>", line 1
    MyClass.class
                ^
SyntaxError: invalid syntax

FWIW,您可以像常規方式那樣定義類方法,然后稍后將它們綁定到類。

警告 :雖然可以使用,但我自己不會使用這種技巧,因為關鍵字class太多了,無法篡改。

您無需命名屬性class ,這會導致各種問題。 您可以命名屬性class_ ,但仍然可以將其從名為class的源屬性中提取出來,並作為class呈現給JSON。

您可以通過重寫序列化器的元類來實現。 這是一個serializers.py文件的示例(模型和類在很大程度上是從教程中直接獲取的)。

主要魔術是元類的這一部分

# Remap fields (to use class instead of class_)
fields_ = []
for name, field in fields:
    if name.endswith('_'):
        name = name.rstrip('_')
    fields_.append((name, field))

這需要你在一個下划線(即結束串定義的任何字段field_ ),當它結合了刪除名稱中使用下划線Fields ,並設置_declared_fields在串行屬性。

from collections import OrderedDict

from rest_framework import serializers
from rest_framework.fields import Field
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES

class MyMeta(serializers.SerializerMetaclass):

    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        fields.sort(key=lambda x: x[1]._creation_counter)

        # If this class is subclassing another Serializer, add that Serializer's
        # fields.  Note that we loop over the bases in *reverse*. This is necessary
        # in order to maintain the correct order of fields.
        for base in reversed(bases):
            if hasattr(base, '_declared_fields'):
                fields = list(base._declared_fields.items()) + fields

        # Remap fields (to use class instead of class_)
        fields_ = []
        for name, field in fields:
            if name.endswith('_'):
                name = name.rstrip('_')
            fields_.append((name, field))

        return OrderedDict(fields_)


class SnippetSerializer(serializers.Serializer):

    __metaclass__ = MyMeta

    pk = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    class_ = serializers.CharField(source='klass', label='class', default='blah')

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.title = validated_data.get('title', instance.title)
        instance.class_ = validated_data.get('class', instance.class_)
        instance.save()
        return instance

這是供參考的models.py文件(django不允許字段名稱以下划線結尾)

from django.db import models

class Snippet(models.Model):
    title = models.CharField(max_length=100, blank=True, default='')
    klass = models.CharField(max_length=100, default='yo')

這是從Django shell看起來的樣子

$ python manage.py shell

>>> from snippets.models import Snippet
>>> from snippets.serializers import SnippetSerializer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser
>>> snippet = Snippet(title='test')
>>> snippet.save()
>>> serializer = SnippetSerializer(snippet)
>>> serializer.data
{'title': u'test', 'pk': 6, 'class': u'yo'}

創建類時無法做到這一點-從技術上講該對象尚不存在。

您可以考慮:

class MyClass(object):
    a = 1
    b = 2

# class is already created
MyClass.__dict__["class"] = 3

但是MyClass.__dict__不是字典,而是dictproxy'dictproxy' object does not support item assignment ,因此會TypeError

完成類定義后,立即使用“''setattr''''設置類屬性。 當然,超出定義。 傳遞'''MyClass'''作為參數,它將創建您的類的屬性。

成員的Dict不應該使用,尤其是在修改對象時。 實際上,幾乎不需要它。 人們通常打算做的大多數事情(盡管不是全部)最好由'''setattr'''和'''getattr'''完成。

最后,正如那些回答的人所注意到的那樣,您實際上並不需要一個名為“''class''''的字段,但這是另一個故事,與您的原始問題不同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM