当前位置: 主页 > 行业资讯 > 大数据培训 > Python中单下划线和双下划线

Python中单下划线和双下划线

2018-03-19 17:46:07 作者:光环大数据 栏目:未知

光环大数据作为国内知名的高端IT就业培训机构,多年来培养无数高薪人才!为了让更多人了解大数据、人工智能、数据分析、python等相关技能,光环大数据免费提供学习视频、2周免费跟班试听课程,如有需要,可点击留言

"单下划线" 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;
"双下划线" 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。

字符串格式化:%和.format

1、首先看使用%格式化文本

常见的占位符:

常见的占位符有:

%d    整数

%f    浮点数

%s    字符串

%x    十六进制整数

使用方法:

>>> 'Hello, %s' % 'world'

'Hello, world'

>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)

'Hi, Michael, you have $1000000.'

使用的时候不知道写什么的地方直接使用 %s 进行代替,语句的末尾加上 %() 括号里面直接填写内容即可(字符串加上引号,中间用“,”分割),如果只有一个%?,括号可以省略。

高级一点的用法:

格式化整数指定是否补零:

首先看代码:

>>> '%d-%d' % (3, 23)

'3-23'

>>> '%2d-%2d' % (3, 23)

' 3-23'

>>> '%3d-%3d' % (3, 23)

'  3- 23'

>>> '%4d-%4d' % (3, 23)

'   3-  23'

>>> '%01d-%01d' % (3, 23)

'3-23'

>>> '%02d-%02d' % (3, 23)

'03-23'

>>> '%03d-%03d' % (3, 23)

'003-023'

>>> '%04d-%04d' % (3, 23)

'0003-0023'

>>>

可以看得出来,d前面的数字用来指定占位符,表示被格式化的数值占用的位置数量(字节还是什么不知道这样的表述是否正确),指定之后比如%3d,代表这个整数要占用3个位置,前面如果有0代表占用的地方使用0补齐,没有就使用空格补齐。指定的空间位置小于实际的数字大小,以实际占用的位置大小为准。

 

指定小数的位数:

>>> '%.f' % 3.1415926
'3'
>>> '%.1f' % 3.1415926
'3.1'
>>> '%.2f' % 3.1415926
'3.14'
>>> '%.3f' % 3.1415926
'3.142'
>>>

可以看出.后面的数字用来表示保留的小数点的位数,".1"代表保留小数点后面一位小数。

如果不确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串:

>>> 'Age: %s. Gender: %s' % (25, True)

'Age: 25. Gender: True'

有些时候,字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%:

>>> 'growth rate: %d %%' % 7

'growth rate: 7 %'

尝试使用其他方法对%进行转义,但是好像没有用,有什么其他方法欢迎评论。

2、使用format 方法进行格式化

代码演示:

age = 25
name = 'Swaroop'

print('{0} is {1} years old'.format(name, age))

print('Why is {0} playing with that python?'.format(name))

位置使用{1}按照使用的顺序写好,后面格式使用 .format()  写好对应的参数即可。

输出结果:

Swaroop is 25 years old

Why is Swaroop playing with that python?

其实也可以使用第一种方法实现:

age = 25

name = 'Swaroop'

print('%s is %s years old'%(name, age))

print('Why is %s playing with that python?'%(name))

输出

Swaroop is 25 years old

Why is Swaroop playing with that python?

 

 

format

它通过{}和:来代替%。
“映射”示例

通过位置

?

1

2

3

4

5

6

In [1]: '{0},{1}'.format('kzc',18)

Out[1]: 'kzc,18'

In [2]: '{},{}'.format('kzc',18)

Out[2]: 'kzc,18'

In [3]: '{1},{0},{1}'.format('kzc',18)

Out[3]: '18,kzc,18'

字符串的format函数可以接受不限个参数,位置可以不按顺序,可以不用或者用多次,不过2.6不能为空{},2.7才可以。
通过关键字参数

?

1

2

In [5]: '{name},{age}'.format(age=18,name='kzc')

Out[5]: 'kzc,18'

通过对象属性

?

1

2

3

4

5

class Person:

  def __init__(self,name,age):

    self.name,self.age = name,age

    def __str__(self):

      return 'This guy is {self.name},is {self.age} old'.format(self=self)

?

1

2

In [2]: str(Person('kzc',18))

Out[2]: 'This guy is kzc,is 18 old'

通过下标

?

1

2

3

In [7]: p=['kzc',18]

In [8]: '{0[0]},{0[1]}'.format(p)

Out[8]: 'kzc,18'

有了这些便捷的“映射”方式,我们就有了偷懒利器。基本的python知识告诉我们,list和tuple可以通过“打散”成普通参数给函数,而dict可以打散成关键字参数给函数(通过和*)。所以可以轻松的传个list/tuple/dict给format函数。非常灵活。
格式限定符

它有着丰富的的“格式限定符”(语法是{}中带:号),比如:

填充与对齐
填充常跟对齐一起使用
^、<、>分别是居中、左对齐、右对齐,后面带宽度
:号后面带填充的字符,只能是一个字符,不指定的话默认是用空格填充
比如

?

1

2

3

4

5

6

In [15]: '{:>8}'.format('189')

Out[15]: '   189'

In [16]: '{:0>8}'.format('189')

Out[16]: '00000189'

In [17]: '{:a>8}'.format('189')

Out[17]: 'aaaaa189'

精度与类型f
精度常跟类型f一起使用

?

1

2

In [44]: '{:.2f}'.format(321.33345)

Out[44]: '321.33'

其中.2表示长度为2的精度,f表示float类型。

其他类型
主要就是进制了,b、d、o、x分别是二进制、十进制、八进制、十六进制。

?

1

2

3

4

5

6

7

8

In [54]: '{:b}'.format(17)

Out[54]: '10001'

In [55]: '{:d}'.format(17)

Out[55]: '17'

In [56]: '{:o}'.format(17)

Out[56]: '21'

In [57]: '{:x}'.format(17)

Out[57]: '11'

用,号还能用来做金额的千位分隔符。

?

1

2

In [47]: '{:,}'.format(1234567890)

Out[47]: '1,234,567,890'

 

迭代器和生成器

1. 迭代器

      迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。

1.1 使用迭代器的优点

      对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。

      另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。

      迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。

迭代器有两个基本的方法

  • next方法:返回迭代器的下一个元素
  • __iter__方法:返回迭代器对象本身

下面用生成斐波那契数列为例子,说明为何用迭代器

代码1

 def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        print b 
        a, b = b, a + b 
        n = n + 1

直接在函数fab(max)中用print打印会导致函数的可复用性变差,因为fab返回None。其他函数无法获得fab函数返回的数列。

代码2

 def fab(max): 
    L = []
    n, a, b = 0, 0, 1 
    while n < max: 
        L.append(b) 
        a, b = b, a + b 
        n = n + 1
    return L

代码2满足了可复用性的需求,但是占用了内存空间,最好不要。

代码3

对比

 for i in range(1000): pass
 for i in xrange(1000): pass

前一个返回1000个元素的列表,而后一个在每次迭代中返回一个元素,因此可以使用迭代器来解决复用可占空间的问题

 class Fab(object): 
    def __init__(self, max): 
        self.max = max 
        self.n, self.a, self.b = 0, 0, 1 
 
    def __iter__(self): 
        return self 
 
    def next(self): 
        if self.n < self.max: 
            r = self.b 
            self.a, self.b = self.b, self.a + self.b 
            self.n = self.n + 1 
            return r 
        raise StopIteration()

执行

1

2

3

4

5

6

7

8

9

>>> for key in Fabs(5):

    print key

 

     

1

1

2

3

5

Fabs 类通过 next() 不断返回数列的下一个数,内存占用始终为常数  

1.2 使用迭代器

使用内建的工厂函数iter(iterable)可以获取迭代器对象:

1

2

3

4

>>> lst = range(5)

>>> it = iter(lst)

>>> it

<listiterator object at 0x01A63110>

使用next()方法可以访问下一个元素:

1

2

3

4

5

6

>>> it.next()

0

>>> it.next()

1

>>> it.next()

2

python处理迭代器越界是抛出StopIteration异常

1

2

3

4

5

6

7

8

9

10

11

12

>>> it.next()

3

>>> it.next

<method-wrapper 'next' of listiterator object at 0x01A63110>

>>> it.next()

4

>>> it.next()

 

Traceback (most recent call last):

  File "<pyshell#27>", line 1, in <module>

    it.next()

StopIteration

了解了StopIteration,可以使用迭代器进行遍历了

lst = range(5)
it = iter(lst)
try:
    while True:
        val = it.next()
        print val
except StopIteration:
    pass

结果

1

2

3

4

5

6

>>>

0

1

2

3

4

事实上,因为迭代器如此普遍,python专门为for关键字做了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。如下

>>> a = (1, 2, 3, 4)
>>> for key in a:
    print key
 
    
1
2
3
4

首先python对关键字in后的对象调用iter函数迭代器,然后调用迭代器的next方法获得元素,直到抛出StopIteration异常。

1.3 定义迭代器

下面一个例子——斐波那契数列

# -*- coding: cp936 -*-
class Fabs(object):
    def __init__(self,max):
        self.max = max
        self.n, self.a, self.b = 0, 0, 1  #特别指出:第0项是0,第1项是第一个1.整个数列从1开始
    def __iter__(self):
        return self
    def next(self):
        if self.n < self.max:
            r = self.b
            self.a, self.b = self.b, self.a + self.b
            self.n = self.n + 1
            return r
        raise StopIteration()
 
print Fabs(5)
for key in Fabs(5):
    print key
    

结果

1

2

3

4

5

6

<__main__.Fabs object at 0x01A63090>

1

1

2

3

5

 

回到顶部

2. 生成器

      带有 yield 的函数在 Python 中被称之为 generator(生成器),几个例子说明下(还是用生成斐波那契数列说明)

可以看出代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果

代码4 

def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

执行

1

2

3

4

5

6

7

8

9

>>> for n in fab(5):

    print n

 

     

1

1

2

3

5

      简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:

1

2

3

4

5

6

7

8

9

10

11

12

13

>>> f = fab(3)

>>> f.next()

1

>>> f.next()

1

>>> f.next()

2

>>> f.next()

 

Traceback (most recent call last):

  File "<pyshell#62>", line 1, in <module>

    f.next()

StopIteration

return作用

在一个生成器中,如果没有return,则默认执行到函数完毕;如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。例如

1

2

3

4

5

6

7

8

9

>>> s = fab(5)

>>> s.next()

1

>>> s.next()

 

Traceback (most recent call last):

  File "<pyshell#66>", line 1, in <module>

    s.next()

StopIteration

代码5  文件读取

 def read_file(fpath): 
    BLOCK_SIZE = 1024 
    with open(fpath, 'rb') as f: 
        while True: 
            block = f.read(BLOCK_SIZE) 
            if block: 
                yield block 
            else: 
                return

如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取

 

*args and **kwargs

单星号形式(*args)用来传递非命名键可变参数列表。双星号形式(**kwargs)用来传递键值可变参数列表。

下面的例子,传递了一个固定位置参数和两个变长参数。

?

1

2

3

4

5

6

def test_var_args(farg, *args):

  print "formal arg:", farg

  for arg in args:

    print "another arg:", arg

 

test_var_args(1, "two", 3)

结果如下:

?

1

2

3

formal arg: 1

another arg: two

another arg: 3

这个例子用来展示键值对形式的可变参数列表,一个固定参数和两个键值参数。

?

1

2

3

4

5

6

def test_var_kwargs(farg, **kwargs):

  print "formal arg:", farg

  for key in kwargs:

    print "another keyword arg: %s: %s" % (key, kwargs[key])

 

test_var_kwargs(farg=1, myarg2="two", myarg3=3)

执行结果:

?

1

2

3

formal arg: 1

another keyword arg: myarg2: two

another keyword arg: myarg3: 3

调用函数时,使用 *args and **kwargs

这种语法不仅仅是在函数定义的时候可以使用,调用函数的时候也可以使用

?

1

2

3

4

5

6

7

def test_var_args_call(arg1, arg2, arg3):

  print "arg1:", arg1

  print "arg2:", arg2

  print "arg3:", arg3

 

args = ("two", 3)

test_var_args_call(1, *args)

执行结果如下:

?

1

2

3

arg1: 1

arg2: two

arg3: 3

键值对方式:

?

1

2

3

4

5

6

7

def test_var_args_call(arg1, arg2, arg3):

  print "arg1:", arg1

  print "arg2:", arg2

  print "arg3:", arg3

 

kwargs = {"arg3": 3, "arg2": "two"}

test_var_args_call(1, **kwargs)

结果如下:

?

1

2

3

arg1: 1

arg2: two

arg3: 3

 

面向切面编程AOP和装饰器

1. 装饰器入门

1.1. 需求是怎么来的?

装饰器的定义很是抽象,我们来看一个小例子。

1

2

3

4

def foo():

    print 'in foo()'

 

foo()

这是一个很无聊的函数没错。但是突然有一个更无聊的人,我们称呼他为B君,说我想看看执行这个函数用了多长时间,好吧,那么我们可以这样做:

1

2

3

4

5

6

7

8

import time

def foo():

    start = time.clock()

    print 'in foo()'

    end = time.clock()

    print 'used:', end - start

 

foo()

很好,功能看起来无懈可击。可是蛋疼的B君此刻突然不想看这个函数了,他对另一个叫foo2的函数产生了更浓厚的兴趣。

怎么办呢?如果把以上新增加的代码复制到foo2里,这就犯了大忌了~复制什么的难道不是最讨厌了么!而且,如果B君继续看了其他的函数呢?

1.2. 以不变应万变,是变也

还记得吗,函数在Python中是一等公民,那么我们可以考虑重新定义一个函数timeit,将foo的引用传递给他,然后在timeit中调用foo并进行计时,这样,我们就达到了不改动foo定义的目的,而且,不论B君看了多少个函数,我们都不用去修改函数定义了!

1

2

3

4

5

6

7

8

9

10

11

12

import time

 

def foo():

    print 'in foo()'

 

def timeit(func):

    start = time.clock()

    func()

    end =time.clock()

    print 'used:', end - start

 

timeit(foo)

看起来逻辑上并没有问题,一切都很美好并且运作正常!……等等,我们似乎修改了调用部分的代码。原本我们是这样调用的:foo(),修改以后变成 了:timeit(foo)。这样的话,如果foo在N处都被调用了,你就不得不去修改这N处的代码。或者更极端的,考虑其中某处调用的代码无法修改这个 情况,比如:这个函数是你交给别人使用的。

1.3. 最大限度地少改动!

既然如此,我们就来想想办法不修改调用的代码;如果不修改调用代码,也就意味着调用foo()需要产生调用timeit(foo)的效果。我们可以 想到将timeit赋值给foo,但是timeit似乎带有一个参数……想办法把参数统一吧!如果timeit(foo)不是直接产生调用效果,而是返回 一个与foo参数列表一致的函数的话……就很好办了,将timeit(foo)的返回值赋值给foo,然后,调用foo()的代码完全不用修改!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#-*- coding: UTF-8 -*-

import time

 

def foo():

    print 'in foo()'

 

# 定义一个计时器,传入一个,并返回另一个附加了计时功能的方法

def timeit(func):

     

    # 定义一个内嵌的包装函数,给传入的函数加上计时功能的包装

    def wrapper():

        start = time.clock()

        func()

        end =time.clock()

        print 'used:', end - start

     

    # 将包装后的函数返回

    return wrapper

 

foo = timeit(foo)

foo()

这样,一个简易的计时器就做好了!我们只需要在定义foo以后调用foo之前,加上foo = timeit(foo),就可以达到计时的目的,这也就是装饰器的概念,看起来像是foo被timeit装饰了。在在这个例子中,函数进入和退出时需要计 时,这被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。与传统编程习惯的从上往下执行方式相比较而言,像是在函数执行的流程中横向地插入了一段逻辑。在特定的业务领域里,能减少大量重复代码。面向切面编程还有相当多的术语,这里就不多做介绍,感兴趣的话可以去找找相关的资料。

这个例子仅用于演示,并没有考虑foo带有参数和有返回值的情况,完善它的重任就交给你了 :)

回到顶部

2. Python的额外支持

2.1. 语法糖

上面这段代码看起来似乎已经不能再精简了,Python于是提供了一个语法糖来降低字符输入量。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

import time

 

def timeit(func):

    def wrapper():

        start = time.clock()

        func()

        end =time.clock()

        print 'used:', end - start

    return wrapper

 

@timeit

def foo():

    print 'in foo()'

 

foo()

重点关注第11行的@timeit,在定义上加上这一行与另外写foo = timeit(foo)完全等价,千万不要以为@有另外的魔力。除了字符输入少了一些,还有一个额外的好处:这样看上去更有装饰器的感觉。

2.2. 内置的装饰器

内置的装饰器有三个,分别是staticmethod、classmethod和property,作用分别是把类中定义的实例方法变成静态方法、 类方法和类属性。由于模块里可以定义函数,所以静态方法和类方法的用处并不是太多,除非你想要完全的面向对象编程。而属性也不是不可或缺的,Java没有 属性也一样活得很滋润。从我个人的Python经验来看,我没有使用过property,使用staticmethod和classmethod的频率也 非常低。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

class Rabbit(object):

     

    def __init__(self, name):

        self._name = name

     

    @staticmethod

    def newRabbit(name):

        return Rabbit(name)

     

    @classmethod

    def newRabbit2(cls):

        return Rabbit('')

     

    @property

    def name(self):

        return self._name

这里定义的属性是一个只读属性,如果需要可写,则需要再定义一个setter:

1

2

3

@name.setter

def name(self, name):

    self._name = name

2.3. functools模块

functools模块提供了两个装饰器。这个模块是Python 2.5后新增的,一般来说大家用的应该都高于这个版本。但我平时的工作环境是2.4 T-T

2.3.1. wraps(wrapped[, assigned][, updated]):
这是一个很有用的装饰器。看过前一篇反射的朋友应该知道,函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数 名foo会变成包装函数的名字wrapper,如果你希望使用反射,可能会导致意外的结果。这个装饰器可以解决这个问题,它能将装饰过的函数的特殊属性保留。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

import time

import functools

 

def timeit(func):

    @functools.wraps(func)

    def wrapper():

        start = time.clock()

        func()

        end =time.clock()

        print 'used:', end - start

    return wrapper

 

@timeit

def foo():

    print 'in foo()'

 

foo()

print foo.__name__

首先注意第5行,如果注释这一行,foo.__name__将是'wrapper'。另外相信你也注意到了,这个装饰器竟然带有一个参数。实际上, 他还有另外两个可选的参数,assigned中的属性名将使用赋值的方式替换,而updated中的属性名将使用update的方式合并,你可以通过查看 functools的源代码获得它们的默认值。对于这个装饰器,相当于wrapper = functools.wraps(func)(wrapper)。

2.3.2. total_ordering(cls):
这个装饰器在特定的场合有一定用处,但是它是在Python 2.7后新增的。它的作用是为实现了至少__lt__、__le__、__gt__、__ge__其中一个的类加上其他的比较方法,这是一个类装饰器。如果觉得不好理解,不妨仔细看看这个装饰器的源代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

53  def total_ordering(cls):

54      """Class decorator that fills in missing ordering methods"""

55      convert = {

56          '__lt__': [('__gt__', lambda self, other: other < self),

57                     ('__le__', lambda self, other: not other < self),

58                     ('__ge__', lambda self, other: not self < other)],

59          '__le__': [('__ge__', lambda self, other: other <= self),

60                     ('__lt__', lambda self, other: not other <= self),

61                     ('__gt__', lambda self, other: not self <= other)],

62          '__gt__': [('__lt__', lambda self, other: other > self),

63                     ('__ge__', lambda self, other: not other > self),

64                     ('__le__', lambda self, other: not self > other)],

65          '__ge__': [('__le__', lambda self, other: other >= self),

66                     ('__gt__', lambda self, other: not other >= self),

67                     ('__lt__', lambda self, other: not self >= other)]

68      }

69      roots = set(dir(cls)) & set(convert)

70      if not roots:

71          raise ValueError('must define at least one ordering operation: < > <= >=')

72      root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__

73      for opname, opfunc in convert[root]:

74          if opname not in roots:

75              opfunc.__name__ = opname

76              opfunc.__doc__ = getattr(int, opname).__doc__

77              setattr(cls, opname, opfunc)

78      return cls

 

 

Aspect Oriented Programming(AOP),面向切面编程,是一个比较热门的话题。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。比如我们最常见的就是日志记录了,举个例子,我们现在提供一个查询学生信息的服务,但是我们希望记录有谁进行了这个查询。如果按照传统的OOP的实现的话,那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现 类 (StudentInfoServiceImpl.java),同时为了要进行记录的话,那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。这样的话,假如我们要实现的服务有多个呢?那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐,而且每个实现类都与记录服务日志的行为紧耦合,违反了面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢?看起来好像就是查询学生的服务自己在进行,但却是背后日志记录对这些行为进行记录,并且查询学生的服务不知道存在这些记录过程,这就是我们要讨论AOP的目的所在。AOP的编程,好像就是把我们在某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,可以就某个功能进行编程。

__new__和__init__的区别

__new__是Python面向对象语言中一个很少用的函数,更多使用的是__init__这个函数。例如:

?

1

2

3

4

5

6

7

8

9

class Book(object):

    def __init__(self, title):

        super(Book, self).__init__(self)

        self.title = title

 

# Define a book

 

b = Book('The Django Book')

print b.title

上面算是OOP语言的入门代码了,粗略一看__init__和Java中的构造函数一样,其实不然,实际上它根本不能算的上构造函数。__new__才是创建实例的方法。

根据官方文档:

·         __init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值。

·         __new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例,是个静态方法。

也就是,__new__在__init__之前被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数。

?

1

2

3

4

5

6

7

8

9

10

11

12

class Book(object):

    def __new__(cls, title):

        print '__new__'

        return super(Book, cls).__new__(cls)

         

    def __init__(self, title):

        print '__init__'

        super(Book, self).__init__(self)

        self.title = title

         

b = Book('The Django Book')

print b.title

上面执行的结果:

?

1

2

3

__new__

__init__

The Django Book

__new__的应用场景

官方文档指出__new__方法的两种用法。

允许继承不可变类型(str,int, tuple)

关于这种也有比较多的例子,网上搜到的例子基本上都属于理论性,实际中用法不太常见。

在MetaClass中使用

MetaClass算是python的语法糖吧,简单来说通过它可以动态生成或更改class的定义。

一个比较实际的例子,是在Django admin 表单验证的时候如何访问当前请求request。StackFlow的链接如下:

http://stackoverflow.com/questions/1057252/how-do-i-access-the-request-object-or-any-other-variable-in-a-forms-clean-met/6062628#6062628

首先想到的是把request也传递过去,在clean方法就可以使用了。

?

1

2

3

4

5

6

7

8

class MyForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):

        self.request = kwargs.pop('request')

        super(MyForm, self).__init__(*args, **kwargs)

     

    def clean(self):

        #这里可以得到self.request的信息

        pass

在平常的view用下面的代码调用:

?

1

f = MyForm(request.POST, request=request)

但是在定制ModelAdmin的时候却不行,因为admin只提供get_form这个方法,返回值是类对象,而不是实例对象

?

1

2

3

4

get_form(self, request, *args, **kwargs):

    # 这行代码是错误的

    # return MyForm(request=request) 

    return MyForm     # OK

用__new__方法可以解决这个问题。

?

1

2

3

4

5

6

def get_form(self, request, *args, **kwargs):

    class ModelFormMetaClass(MyForm):

        def __new__(cls, *args, **kwargs):

            kwargs['request'] = request

            return MyForm(*args, **kwargs)

    return ModelFormMetaClass

那么结果如何呢,add_view的调用代码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

def add_view(self, request, form_url='', extra_context=None)"

    ...

    ModelForm = self.get_form(request)

    if request.method == 'POST':

        form = ModelForm(request.POST, request.FILES)

        #可以获取request参数

        # print form.request

        if form.is_valid():

            pass

        else:

            pass

    else:

        ...(计算initial)

        form = ModelForm(initial=initial)

分析:form = ModelFormMetaClass(request.POST, request.FILES),按照通常的理解右边应该返回的是ModelFormMetaClass的一个实例,由于重写了__new__函数,没有调用父类函数,而是直接返回了一个带有request参数的MyForm实例,然后调用__init__函数,因此最后ModelFormMetaClass()返回也是这个实例,而左边也需要的是MyForm的实例对象。因此__new__函数的作用是创建一个实例。

 

 

 


光环大数据作为国内知名的高端IT就业培训机构,多年来培养无数高薪人才!为了让更多人了解大数据、人工智能、数据分析、python等相关技能,光环大数据免费提供学习视频、2周免费跟班试听课程,如有需要,可点击留言
Tags标签 大数据培训

领取资料

X
立即免费领取

请准确填写您的信息

点击领取