我需要定义一个名为"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 端点必须遵循特定的协议。
有一个为序列化程序定义的元类,它在创建类时调用__new__()
,__new__()
聚合类上定义的所有字段,并用它们填充字段注册表。因此,我必须在创建类之前定义我的变量class
。另请参阅:Django REST 框架:如何使字段的详细名称与其field_name不同?
你可以做:
class MyClass(object):
a = 1
b = 2
vars()['class'] = 3
但是由于class
是一个保留关键字,那么你必须使用 getattr
和 setattr
来访问变量,以便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",它将创建类的属性。
不应使用成员的字典,尤其是在修改对象时。事实上,它很少需要。人们通常打算做的大多数事情(尽管不是全部)最好由"setattr"和"getattr"完成。
最后,正如其中一位回答者所注意到的那样,您实际上并不需要一个名为"类"的字段,但这是另一回事,与您最初的问题不同。