Classes as objects
Before understanding metaclasses, you need to master classes in Python. And Python has a very peculiar idea of what classes are, borrowed from the Smalltalk language.
在理解元类之前,你先要掌握Python的类。Python中的类是借鉴了小众语言的特殊的类。
In most languages, classes are just pieces of code that describe how to produce an object. That's kinda true in Python too:
在多数语言中,类也只不过是用来描述创建对象的代码段,也适用于Python:
>>> class ObjectCreator(object):
pass >>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x000000000357E748>
>>>
But classes are more than that in Python. Classes are objects too.
但是Python中的类不仅仅具有创建对象这一特性,Python中的类还是对象。
Yes, objects.
是的,对象!
As soon as you use the keyword class
, Python executes it and creates an OBJECT. The instruction creates in memory an object with the name "ObjectCreator".
当你使用关键字class时,Python会执行class并创建一个对象。代码在内存中创建一个名为'ObjectCreator'的对象。
This object (the class) is itself capable of creating objects (the instances), and this is why it's a class.
这一对象(类)自身具备创建对象(实例对象)的能力,这就是其被称为类的原因。
But still, it's an object, and therefore: (由于类是对象,因此有以下的特性)
- you can assign it to a variable (赋值给变量)
- you can copy it (复制)
- you can add attributes to it (添加属性)
- you can pass it as a function parameter (作为函数的参数进行传递)
e.g.:
>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
print(o) >>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x000000000357E860>
>>>
1、Creating classes dynamically (动态地创建类)
Since classes are objects, you can create them on the fly, like any object.
因为类是对象,所以你可向其他对象一样自由地创建类
First, you can create a class in a function using class
:
首先,可以在函数中用关键字class创建类:
def choose_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo # return the class, not an instance
else:
class Bar(object):
pass
return Bar >>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.choose_class.<locals>.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.choose_class.<locals>.Foo object at 0x000000000357EDD8>
>>>
But it's not so dynamic, since you still have to write the whole class yourself.
但是以上实例并非动态创建,因为你仍需要手动创建整个类。
Since classes are objects, they must be generated by something.
由于类即对象,对象必须由其他生成。
When you use the class
keyword, Python creates this object automatically. But as with most things in Python, it gives you a way to do it manually.
当你你使用关键字class时,Python自动创建对象。但是在Python中,大部分是手动创建的。
Remember the function type
? The good old function that lets you know what type an object is:
还记得函数type吗?这一经典的函数以下的演示,可以让你对其有一定的理解:
>>> print(type())
<type 'int'>
>>> print(type(''))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
Well, type
has a completely different ability, it can also create classes on the fly. type
can take the description of a class as parameters, and return a class.
type有独特的能力,可以自由地创建类,可以对类的描述作为参数并返回一个类。
(I know, it's silly that the same function can have two completely different uses according to the parameters you pass to it. It's an issue due to backwards compatibility in Python)
type
works this way:
type(name of the class,
tuple of the parent class (for inheritance, can be empty),
dictionary containing attributes names and values)
e.g.:
>>> class MyShinyClass(object):
... pass
can be created manually this way:
>>> MyShinyClass = type('MyShinyClass', (), {}) # return a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x000000000352ED30>
>>>
You'll notice that we use "MyShinyClass" as the name of the class and as the variable to hold the class reference. They can be different, but there is no reason to complicate things.
你可能已经发现,我们将"MyShinyClass"既当成类名,又将其当成变量作为该类的引用。其实是可以用其他变量作为引用的,但是将事情搞得复杂化就罪过了。
type
accepts a dictionary to define the attributes of the class. So:
type可接受一个字典用来定义类属性:
>>> class Foo(object):
... bar = True
Can be translated to: (可以转义为)
Foo = type('Foo', (), {'bar':True})
And used as a normal class:
>>> Foo = type('Foo', (object, ), {'bar':True})
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x000000000352EE80>
>>> print(f.bar)
True
>>>
And of course, you can inherit from it, so:
>>> class FooChild(Foo):
... pass
would be:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True
Eventually you'll want to add methods to your class. Just define a function with the proper signature and assign it as an attribute.
有时可能给类添加方法——只需要一个包含特定处理逻辑的函数并将该函数作为一个属性赋值给类即可。
>>> def echo_bar(self):
print(self.bar) >>> FooChild = type('FooChild', (Foo, ), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True
>>>
And you can add even more methods after you dynamically create the class, just like adding methods to a normally created class object.
当动态地创建类之后,你可添加多个方法给该类,就像你给已创建的类对象添加方法一样。
>>> def echo_bar_more(self):
print('yet another method') >>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True
>>>
You see where we are going: in Python, classes are objects, and you can create a class on the fly, dynamically.
This is what Python does when you use the keyword class
, and it does so by using a metaclass.
由此可见,在Python中,类即对象,你可以动态地,自由地创建类对象。
当你用关键字创建类时,(后台)Python的创建模式采用type的方式,同样使用元类metaclass也是同一原理。
2. What are metaclasses (finally)
Metaclasses are the 'stuff' that creates classes.
元类是创建类的员工。
You define classes in order to create objects, right?
为了创建对象,你得先定义类,对吧?
But we learned that Python classes are objects.
但是Python的类都是对象。(那类又是谁创建的?该不会是石头缝里蹦出来的吧?)
Well, metaclasses are what create these objects. They are the classes' classes, you can picture them this way:
元类就是创建这些类(对象)的东东。元类是类的类,可以用以下的方式加以阐述:
MyClass = MetaClass()
MyObject = MyClass()
You've seen that type
lets you do something like this:
MyClass = type('MyClass', (), {})
It's because the function type
is in fact a metaclass. type
is the metaclass Python uses to create all classes behind the scenes.
实际上函数type就是一个元类。type在后台创建所有的类。
Now you wonder why the heck is it written in lowercase, and not Type
?
可能你会犯嘀咕——这鬼东西(type)为啥要小写,不可以是Type吗?
Well, I guess it's a matter of consistency with str
, the class that creates strings objects, and int
the class that creates integer objects. type
is just the class that creates class objects.
我猜测是为了与str,int等保持一致;因为str()创建的是字符串对象,int()创建的是整型对象,type也不过是创建类对象的类罢了。
You see that by checking the __class__
attribute.
你可以查看一下类中的__class__属性(就知道)。
Everything, and I mean everything, is an object in Python. That includes ints, strings, functions and classes. All of them are objects. And all of them have been created from a class:
在Python中一切皆对象。包括ints,strings,functions及classes它们都是对象。它们都是由一个类创建出来的:
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo():pass
...
>>> foo.__class__
<type 'function'>
>>> class Bar(object):pass
...
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
>>>
Now, what is the __class__
of any __class__
?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
>>>
So, a metaclass is just the stuff that creates class objects.
因此,元类是创建类对象的员工
You can call it a 'class factory' if you wish.
如果你愿意,可以称其为工厂方法
type
is the built-in metaclass Python uses, but of course, you can create your own metaclass.
type是Python中内置的元类,当然,你可以创建自定义元类。
3. The __metaclass__
attribute
You can add a __metaclass__
attribute when you write a class:
创建类时,你可以添加__metaclass__属性:
class Foo(object):
__metaclass__ = something...
[...]
If you do so, Python will use the metaclass to create the class Foo
.
如果你这么干了,Python会借助这个元类创建类Foo。
Careful, it's tricky.
注意,这是一个小花招。
You write class Foo(object)
first, but the class object Foo
is not created in memory yet.
你的确编码了class Foo(object)
,但是此时类对象Foo在内存中并未创建。
Python will look for __metaclass__
in the class definition. If it finds it, it will use it to create the object class Foo
. If it doesn't, it will use type
to create the class.
Python会自动搜寻类中定义的__metaclass__;如果存在,Python即可创建类对象Foo;如果不存在,Python会用type创建该类。
Read that several times.
When you do:
class Foo(Bar):
pass
Python does the following: (Python依次干了以下事情)
Is there a __metaclass__
attribute in Foo
?
类Foo中有__metaclass__属性吗?
If yes, create in memory a class object (I said a class object, stay with me here), with the name Foo
by using what is in __metaclass__
.
有,使用Foo中的__metaclass__在内存中创建类对象。
If Python can't find __metaclass__
, it will look for a __metaclass__
at the MODULE level, and try to do the same (but only for classes that don't inherit anything, basically old-style classes).
没有,Python会在模块级别搜寻__metaclass__,重复以上操作(但仅限于经典类,并且未继承任何类)。
Then if it can't find any __metaclass__
at all, it will use the Bar
's (the first parent) own metaclass (which might be the default type
) to create the class object.
如果还是没找到,Python会使用父类Bar中的元类(可能是默认的type)创建类对象。
Be careful here that the __metaclass__
attribute will not be inherited, the metaclass of the parent (Bar.__class__
) will be. If Bar
used a __metaclass__
attribute that created Bar
with type()
(and not type.__new__()
), the subclasses will not inherit that behavior.
注意:__metaclass__属性是不能被继承的,父类的元类(Bar.__class__)会(用于创建子类对象)。如果Bar由type()创建(非type.__new__()创建),此时子类使用父类Bar中的__metaclass__属性,子类不会继承这一属性。
Now the big question is, what can you put in __metaclass__
?
这就有个疑问,你能在__metaclass__中设置什么?
The answer is: something that can create a class.
答案是:可以创建类的东东。
And what can create a class? type
, or anything that subclasses or uses it.
什么可以创建类?type或是其他。
4. Custom metaclasses(自定义元类)
The main purpose of a metaclass is to change the class automatically, when it's created.
自定义元类的主要目的是,当类创建时,自动改变类。
You usually do this for APIs, where you want to create classes matching the current context.
常用于这样一些API接口——创建符合当前上下文的类。
Imagine a stupid example, where you decide that all classes in your module should have their attributes written in uppercase. There are several ways to do this, but one way is to set __metaclass__
at the module level.
试想一个菜的例子:在你定义的模块中,所有的类的属性需大写。有多种方式做到,其中一个办法便是:设置__metaclass__为模块级别的属性。
This way, all classes of this module will be created using this metaclass, and we just have to tell the metaclass to turn all attributes to uppercase.
这种情况下,该模块下的所有的类创建时均使用这个元类,我们需要做的只是告知元类将所有的类属性全部大写。
Luckily, __metaclass__
can actually be any callable, it doesn't need to be a formal class (I know, something with 'class' in its name doesn't need to be a class, go figure... but it's helpful).
还好,__metaclass__可以实现多种类型的调用,并不需要和普通类一样的调用。
So we will start with a simple example, by using a function.
接下来我们用函数的形式展示这个简单的例子:
# -*- coding:utf-8 -*-
# Author: antcolonies # the metaclass will automatically get passed the same argument
# that you usually pass to `type` def upper_attr(future_class_name, future_class_parents, future_class_attr):
'''
Return a class object, with the list of its attribute
turned into uppercase
'''
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val # let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr) __metaclass__ = upper_attr # this will affect all classes in the module class Foo:
'''
global __metaclass__ won't work with 'object' though
but we can define __metaclass__ here instead to affect
only this class and this will work with 'object' children
'''
bar = 'bip' print(hasattr(Foo, 'bar'))
# output
False
print(hasattr(Foo, 'BAR'))
# output
True f = Foo()
print(f.BAR)
# output
bip
Now, let's do exactly the same, but using a real class for a metaclass:
#-*- coding:utf-8 -*-
# # remember that `type` is actually a class like `str` and
# `int`, so you can inherit from it class UpperAttrMetaclass(type):
'''
__new__ is the method called before __init__
it's the method that creats the object and reurns it
while __init__ just initializes the object passed as parameter
you rarely use __new__, except when you want to control how the object is created
here the created object is the class, and we want to customize it
so we override __new__
you can do some stuff in __init__ as well, but we won't see this
'''
def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type(future_class_name, future_class_parents, uppercase_attr)
But this is not really OOP. We call type
directly and we don't override or call the parent __new__
. Let's do it:
#-*- coding:utf-8 -*-
# # remember that `type` is actually a class like `str` and
# `int`, so you can inherit from it class UpperAttrMetaclass(type):
def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val # reuse the type.__new__ method
# this is basic OPP, nothing magic in there
return type.__new__(upperattr_metaclass,future_class_name, future_class_parents, uppercase_attr)
You may have noticed the extra argument upperattr_metaclass
. There is nothing special about it: __new__
always receives the class it's defined in, as first parameter. Just like you have self
for ordinary methods which receive the instance as first parameter, or the defining class for class methods.
Of course, the names I used here are long for the sake of clarity, but like for self
, all the arguments have conventional names. So a real production metaclass would look like this:
#-*- coding:utf-8 -*-
# class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val # reuse the type.__new__ method
# this is basic OPP, nothing magic in there
return type.__new__(cls,clsname, bases, uppercase_attr)
We can make it even cleaner by using super
, which will ease inheritance (because yes, you can have metaclasses, inheriting from metaclasses, inheriting from type):
#-*- coding:utf-8 -*-
# class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val return super(UpperAttrMetaclass, cls).__new__(clsname, bases, uppercase_attr)
That's it. There is really nothing more about metaclasses.
The reason behind the complexity of the code using metaclasses is not because of metaclasses, it's because you usually use metaclasses to do twisted stuff relying on introspection, manipulating inheritance, vars such as __dict__
, etc.
Indeed, metaclasses are especially useful to do black magic, and therefore complicated stuff. But by themselves, they are simple:
- intercept a class creation (捕获创建的类)
- modify the class (修改类)
- return the modified class (返回修改的类)
5. Why would you use metaclasses classes instead of functions?
Since __metaclass__
can accept any callable, why would you use a class since it's obviously more complicated?
There are several reasons to do so:
- The intention is clear. When you read
UpperAttrMetaclass(type)
, you know what's going to follow - You can use OOP. Metaclass can inherit from metaclass, override parent methods. Metaclasses can even use metaclasses.
- You can structure your code better. You never use metaclasses for something as trivial as the above example. It's usually for something complicated. Having the ability to make several methods and group them in one class is very useful to make the code easier to read.
- You can hook on
__new__
,__init__
and__call__
. Which will allow you to do different stuff. Even if usually you can do it all in__new__
, some people are just more comfortable using__init__
. - These are called metaclasses, damn it! It must mean something!
6. Why would you use metaclasses?
Now the big question. Why would you use some obscure error prone feature?
有一个疑问——为何使用罕见又有错误倾向的特性?
Well, usually you don't:
Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).
Python Guru Tim Peters
The main use case for a metaclass is creating an API. A typical example of this is the Django ORM.
It allows you to define something like this:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
But if you do this:
guy = Person(name='bob', age='35')
print(guy.age)
It won't return an IntegerField
object. It will return an int
, and can even take it directly from the database.
This is possible because models.Model
defines __metaclass__
and it uses some magic that will turn the Person
you just defined with simple statements into a complex hook to a database field.
Django makes something complex look simple by exposing a simple API and using metaclasses, recreating code from this API to do the real job behind the scenes.
6. The last word
First, you know that classes are objects that can create instances.
Well in fact, classes are themselves instances. Of metaclasses.
>>> class Foo(object): pass
>>> id(Foo)
142630324
Everything is an object in Python, and they are all either instances of classes or instances of metaclasses.
Except for type
.
type
is actually its own metaclass. This is not something you could reproduce in pure Python, and is done by cheating a little bit at the implementation level.
Secondly, metaclasses are complicated. You may not want to use them for very simple class alterations. You can change classes by using two different techniques:
- monkey patching
- class decorators
99% of the time you need class alteration, you are better off using these.
But 99% of the time, you don't need class alteration at all.
引用自:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
Classes as objects的更多相关文章
- Reloading Java Classes 101: Objects, Classes and ClassLoaders Translation
The original link: http://zeroturnaround.com/rebellabs/reloading-objects-classes-classloaders/ A Bir ...
- [REPRINT] Java 101: Classes and objects in Java
http://www.javaworld.com/article/2979739/learn-java/java-101-classes-and-objects-in-java.html?page=3 ...
- Think Python - Chapter 15 - Classes and objects
15.1 User-defined typesWe have used many of Python’s built-in types; now we are going to define a ne ...
- Classes and Objects :类和对象(2)
类内部可以有另一个类,也就是内部类,如果带static则为静态内部类静态内部类当然不能直接访问实例变量内部类修饰符可以有四种,而外部类只有两种 内部类的意义:这个内部类只适用于这个外部类因为外部类的某 ...
- Classes and Objects :类和对象(1)
类的定义:修饰符,class,类名,extends,逗号分隔的implements,类体{}规范的类名:首字母要大写,以后每个单词首字母都大写字段的定义:修饰符,类型,字段名按照封装的思想,字段通常定 ...
- Programming C#.Classes and Objects.成员方法
this关键字指向类的当前实例,this指针是类中所有非静态方法的隐藏指针,每个方法都能通过this指针指向对象的其他方法和成员变量. 因为对一个类来说,它的成员函数(方法)只有一份,所有的实例对象共 ...
- Programming C#.Classes and Objects.传递参数
ref 关键字通过引用(而非值)传递参数. 通过引用传递的效果是,对所调用方法中的参数进行的任何更改都反映在调用方法中. 说明: 不要混淆通过引用传递的概念与引用类型的概念. 这两种概念是不同的. 无 ...
- Programming C#.Classes and Objects.只读字段
只读字段 当字段声明中含有 readonly 修饰符时,该声明所引入的字段为只读字段.给只读字段的直接赋值只能作为声明的组成部分出现,或在同一类中的实例构造函数或静态构造函数中出现.(在这些上下文中, ...
- Kotlin Reference (八) Classes and Objects
most from reference 类 Kotlin的类的声明使用关键字class class Invoice { } 类声明由类名.类头(指定其类型参数,构造函数等)和类体组成,由大括号括起来. ...
随机推荐
- matlab任务:FCM分类
一个朋友让帮忙做图像分类,用FCM聚类算法,网上查了一下,FCM基本都是对一幅图像进行像素的分类,跟他说的任务不太一样,所要做的是将一个文件夹里的一千多幅图像进行分类.图像大概是这个样子的(是25*2 ...
- tftp命令详解
TFTP协议简介TFTP是用来下载远程文件的最简单网络协议,它其于UDP协议而实现.嵌入式linux的tftp开发环境包括两个方面: 一是linux服务器端的tftp-server支持,二是嵌入式目标 ...
- Linux常用命令分类总结
文件及文件夹操作 新建文件 vi file 新建文件夹 mkdir dirName 复制文件 cp sourceFile targetFile 移动文件 mv file targetDir 文件或文件 ...
- 调试.NET CORE代码
前言 core也用了很长一段时间了,发现很多小伙伴不知道如何调试core的代码. 可想而知,以前使用mvc的时候,不需要发布代码,直接iis地址指向项目源码,然后附加到进程w3wp.exe就可以调试了 ...
- bootstrap学习(四)输入框、导航
输入框组: 基本用法: //form-control 占满 //input-group:输入框组//input-group-addon:输入框前加入一个前缀 <div class="i ...
- java基础之变量和常量、类型转换
一. 变量 变量是可改变的量,每赋个值便会开辟一个新内存地址. 1.首先,变量需要一个声明,例如:int a,这个a也可以当作是一个标签,它指向了一个内存地址,这个地址是属于int类型的套餐, ...
- loj #108. 多项式乘法
#108. 多项式乘法 题目描述 这是一道模板题. 输入两个多项式,输出这两个多项式的乘积. 输入格式 第一行两个整数 n nn 和 m mm,分别表示两个多项式的次数. 第二行 n+1 n + ...
- CI框架源码学习笔记6——Config.php
接着上一节往下,我们这一节来看看配置类Config.php,对应手册内容http://codeigniter.org.cn/user_guide/libraries/config.html. clas ...
- P2115 [USACO14MAR]破坏Sabotage
题意:给你一个正整数序列,让你删去一段区间内的数[l,r] $1<l\le r <n$ 使得剩余的数平均值最小$n\le 10^5$ 1.不难想到暴力,用前缀和优化$O(n^2)$ #in ...
- poj3321(dfs序+树状数组)
题目链接:https://vjudge.net/problem/POJ-3321 题意:给一个普通树(不是二叉树),并且已经编号,每个结点为1或0,有两种操作,对单个结点修改和查询一个结点的子树的所有 ...