Python高级特性

1 内建函数(__xxx__)

    背景:为什么要有这种带下划线的内建函数,个人认为这种内建函数开放了很多Python的特殊用法,只要详尽掌握,就会理解平时用到的数据结构是复写了什么方法,自己也可以写出类似set,dqueue,dict,list的数据类型方法。

    1.1 类似字典的操作方式,__getitem__,__setitem__,__delitem__

        我们熟悉的字典是可以像如下方式操作赋值取值:

d = dict()
d["a"] = 1
d["b"] = 2
print d
del d["a"]
print d

       其实像这种赋值方式就是复写了__setitem__,__getitem__,__delitem__请见如下类

class TestDict(object):

   def __getitem__(self,key):
       return self.__dict__.get(key)

   def __setitem__(self,key,value):
       self.__dict__[key] = value

   def __delitem__(self,key):
       self.__dict__.pop(key)

td = TestDict()
td["a"] = 1
td["b"] = 2
print td["a"]
print td.__dict__
del td["a"]
print td.__dict__

为了更加灵活,如上代码也可以在对象执行赋值方法(__init__)的时候声明个变量d = dict(),然后对这个变量进行赋值和取值操作也可以模拟上面的操作。

    1.2 __new__和__init__

        __new__: 在类实例化的时候调用,用来创建实例如果不返回实例那么__init__将不会执行,第一个参数是class对象,在创建实例的时候需要有返回值

        __init__: 在初始化实例的时候调用,比如说实例属性赋值,第一个参数是实例对象,一般都重写__init__方法,在执行的时候不需要返回值

class TestNew(object):
    def __new__(cls, *args, **kwargs):
        print '__new__ called.'
        return super(TestNew,cls).__new__(cls,*args,**kwargs)
    def __init__(self):
        print '__init__ called.'
        self.a = 1

tn = TestNew()
print tn.a

       可以用__new__来实现单例模式

 

class Singleton(object):
        def __new__(cls):
            # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
            if not hasattr(cls, 'instance'):
                cls.instance = super(Singleton, cls).__new__(cls)
            return cls.instance

obj1 = Singleton()
obj2 = Singleton()

obj1.attr1 = 'value1'
print obj1.attr1, obj2.attr1
print obj1 is obj2

Tips: 单例模式有很多种实现方式,也可以通过类变量+静态方法的方式实现。

    可以通过重载__new__来实现很多创建实例时的功能。

    1.3 __iter__,next(python2),__next__(python3)

                      __iter__: 复写这个函数的对象是可迭代对象

            next/__next__: 复写这个函数的对象都是一个迭代器

 

class TestIterNext(object):
    def __init__(self,data=1):
        self.data = data

    def next(self):
        if self.data > 5:
            raise StopIteration
        else:
            self.data+=1
            return self.data

    def __iter__(self):
        print "iter"
        return self

当for循环去迭代tin = TestIterNext()对象的时候第一步会去看__iter__是否返回一个生成器(generator),如果返回的是对象本身才会去执行next函数。

    1.4 __call__

            把一个类实例的对象当做函数一样调用就是复写了__call__方法,如下:

class TestCall(object):
    def __call__(self):
        print "call it"
tc = TestCall()
tc()

            复写__call__方法大多数用在装饰器类中(第5章)和继承Type类(第3章)的时候。

    1.5 __repr__,__str__

        __repr__和__str__没有太大区别,在Python交互模式下才能发现。复写改方法后输出对象就是按照该方法里的内容进行输出。在类里可以这样用__repr__ = __str__

    1.6 __all__

        可用于模块导入时限制,,当我们from module import *的时候这个__all__就起作用了,__all__=["bar","sar"] ,[]里定义函数或者变量类等,有些模块内部一些函数不对外开放,此时把一些对外开放的函数变量放入到__all__里就可以了,这样避免了一些多余的导入。如果在__init__.py里定义则在导入模块的时候只导入__all__里定义的各个文件,此时无法定义到文件里具体哪个类或方法,如果需要细化则需要在具体的类里写入__all__。如果是from module import Test这种使用不受__all__限制

    1.7 __setattr__,__getattr__,__delattr__

            __setattr__: 对变量赋值时调用。

            __getattr__:默认查找对象属性是在一个字典里(__dict__),这里没有要查找的对象则去__getattr__方法里查找,如果我们复写__getattr__则可以根据实际需求来返回值。

            __delattr__:删除属性时调用。

class TestAttr(object):
    def __init__(self):
        self.name = "abc"

    def __getattr__(self, item):
        print "item:" + str(item)
        print "getattr"
        return 10
    def __setattr__(self, *args, **kwargs):
        print "set attr"
        object.__setattr__(self,*args,**kwargs)

    def __delattr__(self, *args, **kwargs):
        print "delete attr"
        object.__delattr__(self,*args, **kwargs)

ta = TestAttr()
print ta.__dict__
print ta.names
del ta.name
print ta.__dict__

 

    1.8 __le__,__lt__,__ge__,__gt__,__ne__,__eq__

           一个对象和另一个对象比较大小,返回的并不一定是True和False,返回值有可能是我们定义的任何值,这里就是复写上面这些方法。如下:

 

class TestCompare(object):
    def __lt__(self, other):
        return "aaa"

t = TestCompare()
print t<1

这里打印出的就不是True和False,打印的是我们再复写的方法里定义的"aaa",其实有一些常用的orm,比如说sqlalchemy里查询条件可以这样写

 

g.pg_db.query(Company.id).filter(Company.level_id == level)

这里的Company.level_id == level返回的就是筛选条件,因为对Company的level_id对象复写了__eq__

    1.9 __slots__

 

        优点:

            1,更快的属性访问速度

            2,减少内存消耗

 

        每个类里都维护一个字典__dict__,这个字典维护了对象的所有属性,但如果成千上万个对象则就会创建很多个__dict__来存放对象属性,为了性能我们可以不用Python帮我们维护这个字典。此时我们在类里定义__slots__ = ["name","age"]的时候就表示禁用了__dict__,并限定name和age为类的属性(类里只能有name和age属性),这样做的好处能大大节省内存开支,对象越多节省的就越多,大概能节省40%以上。

 

class TestSlots(object):
    __slots__ = ["name","age"]
    def __init__(self, name, age):
        self.name = name
        self.age = age

ts = TestSlots("a",1)
ts.name = 1
print ts.name

    2.0 __metaclass__

        在理解元类之前,你需要先掌握Python中的类。Python中类的概念借鉴于Smalltalk,这显得有些奇特。在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立

        元类是什么?

 

            但就元类本身而言,它们其实是很简单的:

            1)   拦截类的创建

            2)   修改类

            3)   返回修改之后的类

 

        但是,Python中的类还远不止如此。类同样也是一种对象。是的,没错,就是对象。只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。下面的代码段:

 

class ObjectCreator(object):
    pass

 

将在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:

 

1)   你可以将它赋值给一个变量

2)   你可以拷贝它

3)   你可以为它增加属性

4)   你可以将它作为函数参数进行传递

下面是示例:

 

print ObjectCreator
# 你可以打印一个类,因为它其实也是一个对象
def echo(o):
    print o

echo(ObjectCreator)

# 你可以将类做为参数传给函数
print hasattr(ObjectCreator, 'new_attribute')
ObjectCreator.new_attribute = 'foo'
#  你可以为类增加属性
print hasattr(ObjectCreator, 'new_attribute')
print ObjectCreator.new_attribute
ObjectCreatorMirror = ObjectCreator
# 你可以将类赋值给一个变量
print ObjectCreatorMirror()

动态地创建类

因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用class关键字即可。

 

def choose_class(name):
    if name == 'foo':
        class Foo(object):
            pass
        return Foo     
# 返回的是类,不是类的实例
    else:
        class Bar(object):
            pass
        return Bar
MyClass = choose_class('foo')
print MyClass              
# 函数返回的是类,不是类的实例>>> print MyClass()            
# 你可以通过这个类创建类实例,也就是对象
# <__main__.foo object="" at="" 0x89c6d4c="">

但这还不够动态,因为你仍然需要自己编写整个类的代码。由于类也是对象,所以它们必须是通过什么东西来生成的才对。当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。还记得内建函数type吗?这个古老但强大的函数能够让你知道一个对象的类型是什么,就像这样:

 

print type(1)
print type("1")
print type(ObjectCreator)
print type(ObjectCreator())

这里,type有一种完全不同的能力,它也能动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。(我知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)

type可以像这样工作:

type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

比如下面的代码:

class MyShinyClass(object):
    pass

MyShinyClass = type('MyShinyClass', (), {})  
# 返回一个类对象
print MyShinyClass
print MyShinyClass()  
#  创建一个该类的实例
# <__main__.myshinyclass object="" at="" 0x8997cec="">

你会发现我们使用“MyShinyClass”作为类名,并且也可以把它当做一个变量来作为类的引用。类和变量是不同的,这里没有任何理由把事情弄的复杂。

type 接受一个字典来为类定义属性,因此

class Foo(object):
    bar = True
# 可以翻译为:

Foo = type('Foo', (), {'bar':True})

为类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。

 

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

 你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。

 

到底什么是元类

元类就是用来创建类的“东西”。你创建类就是为了创

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值