制作ip池的时候,可能需要不定时新代理池获取的网站,通常的做法是每次添加一个新的网站,就改一下获取函数,这样更改添加方法很容易出错.我们可以设想另外一种模式,当我们要添加新的获取网站的时候,我们只要添加对应的函数就可以了,而不需要更改其他代码,这样会方便很多,这里我们借助元类来实现.
__metaclass__ 的一些理解
以下是我个人理解:
- python中,一切皆对象,元类也是一个对象
- 元类是可以创造类对象的一种类
- type除了显示对象类型以外,还可以作为关键字去创建类
参考别人的资料得到的信息:
-
类也是对象,当使用关键字class的时候python解释器就会自动的取创建一个对象,这个对象自身拥有创建对象(类实例,也就是我们说的类实例化出来的对象)的能力,它的本质仍然是一个对象,于是你可以对它进行如下操作:
- 你可以把它复制给一个变量
- 你可以copy它
- 你可以为它增加属性
- 你可以将他作为参数进行传递
可以动态的在方法中创建类,但是仍然需要你自己编写整个类的方法和属性,如下列子:
def choose_class(name):
if name == 'foo':
class Foo(object): # 这里就是动态的根据条件来创建类但是关于它的方法仍然需要自己填写完整才能继续
pass
return Foo # 返回的是类,不是类的实例
else:
class Bar(object):
pass
return Bar
除了手动创建类以外typey可以可以创建类的,格式如下:
type(类名,父类的元祖(在有继承的情况下,可以为空),包含属性的字典(名称和值))
比如我们要创建一个名为TestFunc(object)的类,它有一个属性叫做name 值是 TestFunc, 那么我们可以按照下面的方式创建:
type(TestFunc,(object),{'name':'TestFunc'})
这样我们就使用表达式创建了一个类,这个类和上面的模式使用class创建的类是没有区别的.在这里发现可以批量性的根据自己的需要来创建类了.
__metaclass__属性
这是一个类属性,当你用这个属性创建类的时候,它就会用元类来创建类.
class TestFunc(AClass):
__metaclass__=someting
如果你这么做了,Python就会用元类来创建类Foo。这里面可以这样理解。首先写下class Foo(object),但是类对象Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。把下面这段话反复读几次。当你写如下代码时:
class TestFunc(Aclass):
pass
Python做了如下的操作:
Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
你可以在metaclass中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的都可以。
下面就回归到了我们开篇说的问题如何子在不修改类已经存在的方法的基础上扩展我们的类,答案是 -- 自定义元类
我们获取类中所有以newfunc开头的函数
class NewMetaclass(type):
def __new__(cls, name, bases, attrs):
count = 0
attrs['__NewFunc__'] = []
for k, v in attrs.items():
if 'newfunc' in k:
attrs['__NewFunc__'].append(k)
count += 1
attrs['__NewFuncCount__'] = count
return type.__new__(cls, name, bases, attrs)
class NewFunc(object, metaclass=NewMetaclass):
def newfunc_a(self):
return 'a1'
def new_a(self):
return 'a2'
def newfunc_b(self):
return 'b1'
def new_b(self):
return 'b2'
test = NewFunc
count = test.__NewFuncCount__
content = test.__NewFunc__
print(count, content)
'''
运行结果如下:
2 ['newfunc_a', 'newfunc_b']
'''
这里我们解释下几个参数,这里对应着我们上面说的使用type创建类的参数:
type(类名,父类的元祖(在有继承的情况下需要写出来,其他可以为空),包含属性的字典(名称和值))
参数 | 含义 |
---|---|
cls | 类的实例对象 |
name | 类的名字 |
bases | 类所有继承的类 |
attrs | 包含属性的字典 |
我们为什么要使用元类?引用Tim Peters的一句话:
“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters
其实我们使用元类更多的就是为了创建API,让功能更加方便
参考文章: 深度理解python中的元类
eval() 函数的使用
eval()官方文档里面给出来的功能解释是:将字符串string对象转化为有效的表达式参与求值运算返回计算结果
语法上:eval(expression,globals=None, locals=None)
返回的是计算结果
参数的含义:
- expression是一个参与计算的python表达式
- globals是可选的参数,如果设置属性不为None的话,就必须是dictionary对象了
- locals也是一个可选的对象,如果设置属性不为None的话,可以是任何map对象了
python是用命名空间来记录变量的轨迹的,命名空间是一个dictionary,键是变量名,值是变量值。
当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
1)局部名字空间 - 特指当前函数或类的方法。如果函数定义了一个局部变量 x, 或一个参数 x,Python 将使用它,然后停止搜索。
2)全局名字空间 - 特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。
3)内置名字空间 - 对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。
python的全局名字空间存储在一个叫globals()的dict对象中;局部名字空间存储在一个叫locals()的dict对象中。我们可以用print (locals())来查看该函数体内的所有变量名和变量值。
也就是说我们给它传入一个字符串,它会取执行这个字符串所代表的函数
demo:
def change():
print('this is change func')
funcname = '{}()'.format('change')
print(funcname)
eval(funcname)
'''
结果如下:
change()
this is change func
'''
当然这个函数使用起来也有很大的风险,这里不再详细说明给几个网址大家可以研究下: