python:如何从类变量访问类.__ dict__?

时间:2016-07-12 09:48:33

标签: python django django-rest-framework

我需要定义一个名为" 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 Framework序列化器,我需要一个"类"要在其上定义的字段,因为此REST端点必须遵循某个协议。

为Serializers定义了一个元类,它在创建类时调用__new__()__new__()聚合所有字段,在类上定义并用它们填充字段注册表。所以,我必须在创建类之前定义我的变量class。另请参阅:Django REST Framework: how to make verbose name of field differ from its field_name?

5 个答案:

答案 0 :(得分:1)

你可以这样做:

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

但由于class是保留关键字,因此您必须使用getattrsetattr访问该变量,以便class仍为字符串。

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

答案 1 :(得分:1)

您可以从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太多关键字无法篡改。

答案 2 :(得分:1)

您不需要为属性class命名,这可能会导致各种问题。您可以为属性class_命名,但仍然可以从名为class的源属性中提取它,并将其呈现为class的JSON。

您可以通过覆盖Serializers的元类来完成此操作。以下是serializers.py文件的示例(模型和类主要是从tutorial直接拉出来的。)

主要的魔力是元类的这一部分

# 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'}

答案 3 :(得分:0)

创建类时不能这样做 - 从技术上讲,该对象尚不存在。

您可以考虑:

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

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

MyClass.__dict__不是dict,而是dictproxy'dictproxy' object does not support item assignment,因此会TypeError被提升。

答案 4 :(得分:0)

使用'''setattr'''在完成类定义后立即设置类属性。当然,在定义之外。传递'''MyClass'''作为参数,它将创建你的类的属性。

不应使用成员的字典,特别是对于修改对象。事实上,它很少需要。人们通常打算做的事情(尽管不是全部)通过'''setattr'''和'''getattr'''更好地完成。

最后,正如其中一位回答者注意到的那样,你并不需要一个名为'''class'''的字段,但这是另一个故事,与你原来的问题不同。