什么是甲状腺?它们用于什么?
当前回答
甲特克拉斯(甲特克拉斯)是一类,讲述了(某些)其他类应该是如何形成的。
这是一个案例,我看到甲状腺作为解决我的问题:我有一个真正复杂的问题,可能可以是不同的解决,但我选择用甲状腺解决它。 由于复杂性,这是我写的几个模块之一,在模块上的评论超过了编写的代码的数量。
#!/usr/bin/env python
# Copyright (C) 2013-2014 Craig Phillips. All rights reserved.
# This requires some explaining. The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried. I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to. See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType. This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient. The complicated bit
# comes from requiring the GsyncOptions class to be static. By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace. Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet. The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method. This is the first and only time the class will actually have its
# dictionary statically populated. The docopt module is invoked to parse the
# usage document and generate command line options from it. These are then
# paired with their defaults and what's in sys.argv. After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored. This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times. The __getattr__ call hides this by default, returning the
# last item in a property's list. However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
class GsyncListOptions(object):
__initialised = False
class GsyncOptionsType(type):
def __initialiseClass(cls):
if GsyncListOptions._GsyncListOptions__initialised: return
from docopt import docopt
from libgsync.options import doc
from libgsync import __version__
options = docopt(
doc.__doc__ % __version__,
version = __version__,
options_first = True
)
paths = options.pop('<path>', None)
setattr(cls, "destination_path", paths.pop() if paths else None)
setattr(cls, "source_paths", paths)
setattr(cls, "options", options)
for k, v in options.iteritems():
setattr(cls, k, v)
GsyncListOptions._GsyncListOptions__initialised = True
def list(cls):
return GsyncListOptions
def __getattr__(cls, name):
cls.__initialiseClass()
return getattr(GsyncListOptions, name)[-1]
def __setattr__(cls, name, value):
# Substitut option names: --an-option-name for an_option_name
import re
name = re.sub(r'^__', "", re.sub(r'-', "_", name))
listvalue = []
# Ensure value is converted to a list type for GsyncListOptions
if isinstance(value, list):
if value:
listvalue = [] + value
else:
listvalue = [ None ]
else:
listvalue = [ value ]
type.__setattr__(GsyncListOptions, name, listvalue)
# Cleanup this module to prevent tinkering.
import sys
module = sys.modules[__name__]
del module.__dict__['GetGsyncOptionsType']
return GsyncOptionsType
# Our singlton abstract proxy class.
class GsyncOptions(object):
__metaclass__ = GetGsyncOptionsType()
其他回答
Python 3 更新
在一个甲状腺中,有(目前)两个关键方法:
__prepare__ 允许您提供自定义地图(如 OrderedDict)作为名称空间使用,而类正在创建。
__new__ 负责最终类的实际创建/修改。
一个色彩色彩,不做任何东西 - 额外的金属类会喜欢:
class Meta(type):
def __prepare__(metaclass, cls, bases):
return dict()
def __new__(metacls, cls, bases, clsdict):
return super().__new__(metacls, cls, bases, clsdict)
一个简单的例子:
说你想要一些简单的验证代码在你的属性上运行 - 因为它必须总是一个 int 或 str. 没有一个 metaclass,你的类会看起来像:
class Person:
weight = ValidateType('weight', int)
age = ValidateType('age', int)
name = ValidateType('name', str)
正如你可以看到的那样,你必须重复属性的名称两次,这使得类型与刺激的错误一起可能。
一个简单的甲状腺可以解决这个问题:
class Person(metaclass=Validator):
weight = ValidateType(int)
age = ValidateType(int)
name = ValidateType(str)
class Validator(type):
def __new__(metacls, cls, bases, clsdict):
# search clsdict looking for ValidateType descriptors
for name, attr in clsdict.items():
if isinstance(attr, ValidateType):
attr.name = name
attr.attr = '_' + name
# create final class and return it
return super().__new__(metacls, cls, bases, clsdict)
一个样本运行:
p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'
生产:
9
Traceback (most recent call last):
File "simple_meta.py", line 36, in <module>
p.weight = '9'
File "simple_meta.py", line 24, in __set__
(self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')
注意:这个例子是简单的,它也可能已经完成了一个类装饰师,但假设一个真正的金属玻璃会做得更多。
class ValidateType:
def __init__(self, type):
self.name = None # will be set by metaclass
self.attr = None # will be set by metaclass
self.type = type
def __get__(self, inst, cls):
if inst is None:
return self
else:
return inst.__dict__[self.attr]
def __set__(self, inst, value):
if not isinstance(value, self.type):
raise TypeError('%s must be of type(s) %s (got %r)' %
(self.name, self.type, value))
else:
inst.__dict__[self.attr] = value
什么是Metaclasses?你用它们用于什么?
>>> Class(...)
instance
>>> Metaclass(...)
Class
>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>
每当你创建一个类时,你都会使用一个类型:
class Foo(object):
'demo'
>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)
name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__module__': '__main__', '__weakref__': <attribute '__weakref__'
of 'Foo' objects>, '__doc__': 'demo'})
(在 __dict__: __module__ 类的内容上有一个侧笔记,因为类必须知道它们在哪里定义,而 __dict__ 和 __weakref__ 是因为我们不定义 __slots__ - 如果我们定义 __slots__ 我们会在例子中节省一些空间,因为我们可以通过排除它们来排除 __dict__ 和 __weakref__。
>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})
我们可以像任何其他类定义一样扩展类型:
>>> Foo
<class '__main__.Foo'>
class Type(type):
def __repr__(cls):
"""
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> eval(repr(Baz))
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
"""
metaname = type(cls).__name__
name = cls.__name__
parents = ', '.join(b.__name__ for b in cls.__bases__)
if parents:
parents += ','
namespace = ', '.join(': '.join(
(repr(k), repr(v) if not isinstance(v, type) else v.__name__))
for k, v in cls.__dict__.items())
return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
def __eq__(cls, other):
"""
>>> Baz == eval(repr(Baz))
True
"""
return (cls.__name__, cls.__bases__, cls.__dict__) == (
other.__name__, other.__bases__, other.__dict__)
>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
但是,与 eval(repr(Class))的进一步检查是不可能的(因为函数将是相当不可能从他们的默认 __repr__ 的 eval 。
from collections import OrderedDict
class OrderedType(Type):
@classmethod
def __prepare__(metacls, name, bases, **kwargs):
return OrderedDict()
def __new__(cls, name, bases, namespace, **kwargs):
result = Type.__new__(cls, name, bases, dict(namespace))
result.members = tuple(namespace)
return result
class OrderedMethodsObject(object, metaclass=OrderedType):
def method1(self): pass
def method2(self): pass
def method3(self): pass
def method4(self): pass
>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')
>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)
而且它大约有正确的回报(除非我们能找到代表我们的功能的方式,否则我们就不能再评估):
>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})
我看到一个有趣的使用案例在一个名为类用途的包中,它检查是否所有类变量在顶部案例格式(方便有统一逻辑的配置类),并检查是否没有例子级方法在课堂上。
此分類上一篇: tl;dr version
类型(obj)函数会给你一个对象的类型。
一个阶级的类型( )是它的甲型阶级。
使用甲状腺:
class Foo(object):
__metaclass__ = MyMetaClass
一个类的类是一个类的类 - 一个类的身体是转移到一个类的论点,它被用来构建一个类。
在这里,你可以阅读如何使用金属玻璃来自定义课堂建筑。
在Python中,一类是指一个子类的子类,它决定一个子类的行为方式;在Python中,一类是另一个子类的例子;在Python中,一类是指一个子类的例子将如何行事。
由于甲基层负责类型,所以你可以写自己的自定义甲基层来改变类型是通过执行额外的操作或注射代码创建的方式。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录