Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1132783
  • 博文数量: 254
  • 博客积分: 1242
  • 博客等级: 少尉
  • 技术积分: 1581
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-03 21:49
文章分类

全部博文(254)

文章存档

2017年(16)

2016年(4)

2013年(94)

2012年(140)

分类: Python/Ruby

2017-10-22 09:47:46

(原文是 Python's Magical Self ,来自  )

的self参数有时真让人抓狂,比如,你必须在每一个类的方法里显示定义self,然后,它会霸占不需要它们的地方。            

[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. class Foo(object):  
  2.     x = 9  
  3.     def __init__(self,x):  
  4.         self.x = x  
  5.    
  6.     def bar(self,y):  
  7.         return self.x + y  


            如果你有C++,Java或其他语言的编程背景,你会觉得 __init__ 和 bar 方法里的self 看起来很多余,python不是经常吹嘘自己的简答和优雅吗,self到底有什么用?

作用域出现了

           在python里,作用域是非常简单的。Python里的一切都是对象,几乎任何东西都是在对象水平的作用域里。写一个模块试试?


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. # test.py  
  2. def say_hi():  
  3.     print 'Hi!'  

          你刚刚创建了一个新的带有say_hi属性的模块对象。


          定义一个类?


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. class Foo(object):  
  2.     x = 9  
  3.     def __init__(self,x):  
  4.         self.x = x  
  5.    
  6.     def bar(self,y):  
  7.         return self.x + y  


         你刚刚写了一个带有一些属性的类对象,那些属性是 x,__init__ ,还有 bar。


         实例化Foo?


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. foo = Foo(5)  


         你创建了一个带有属性x,__init__ ,和bar的Foo 实例,请记住,foo的三个属性跟Foo的不一样,待会,你就会知道为什么。


上下文就是一切


         把Foo拆开:


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. def bar(self,y):  
  2.     return self.x + y  
  3.    
  4. class Foo(object):  
  5.     x = 9  
  6.     def __init__(self,x):  
  7.         self.x = x  
  8.     bar = bar  


         先不理bar的第一参数self,如果我们单单把bar看作普通的函数,那么,接下来的就很合理了。

[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. foo = Foo(5)  
  2.    
  3. print bar(foo,4) == 9  
  4. print bar(Foo,0) == 9  
         好像Foo.bar也可以这么做。


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. print Foo.bar(foo,4) == 9  
  2. print Foo.bar(Foo,0) == 9  


        第一行打印出结果True,但第二行就出现了类型错误TypeError):未绑定的方法bar必须用Foo实例作为第一个参数(出现了不匹配的类型对象)。实例化一个Foo,并且修改bar,把self参数隐藏掉。


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. print foo.bar(foo,4) == 9  
  2. print foo.bar(foo,0) == 9  
        两行代码都出现 类型错误(TypeError):bar() 需要两个参数(出现了3个)。为什么是2个,而不是3个?答案即将揭晓。

绑定self



        如果你查一下三个bar的类型,你会发现,他们不全都一样。


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. print type(bar)  
  2.   
  3. print type(Foo.bar)  
  4.   
  5. print type(foo.bar)  
  6.   
        把任何函数绑定到一个实例方法对象里,并把它封装在一个实例方法对象里,实例方法就会像胶水一样,粘着类、实例对象和原始的函数,最终它们都绑在一起。
[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. print Foo.bar.im_class == Foo  
  2. print Foo.bar.im_func == bar  
  3. print Foo.bar.im_self == None  
  4. print foo.bar.im_class == Foo  
  5. print foo.bar.im_func == bar  
  6. print foo.bar.im_self == foo  


        可以直截了当地用python写一个实例方法类。


[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. class myinstancemethod(object):  
  2.     def __init__(self,func,cls,instance=None):  
  3.         self.im_func = func  
  4.         self.im_class = cls  
  5.         self.im_self = instance  
  6.    
  7.     def __call__(_self,*args,**kwargs):  
  8.         args = list(args)  
  9.         if _self.im_self is not None:  
  10.             args.insert(0,_self.im_self)  
  11.                
  12.         if len(args) == 0:  
  13.             raise TypeError("unbound method bar() must be called with Foo instance as first argument (got nothing instead)")  
  14.         elif not isinstance(args[0],_self.im_class):  
  15.             raise TypeError("unbound method bar() must be called with Foo instance as first argument (got %s instead)" % type(args[0]).__name__)  
  16.         else:  
  17.             return _self.im_func(*args,**kwargs)  


          myinstancemethod 很正确地模仿了实例方法类,它跟前面的foo.bar 和Foo.bar的表现一样,除了它处理了一点类边缘情况和实例方法调用。

[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. my_unbound(self=foo,y=4)  
  2. # TypeError: bar() got multiple values for keyword argument 'self'  
  3. Foo.bar(self=foo,y=4)  
  4. # TypeError: bar() got multiple values for keyword argument 'self'  
  5.    
  6. my_bound(self=foo,y=4)  
  7. # TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)  
  8. foo.bar(self=foo,y=4)  
  9. # TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)  

           这就是为什么你能够传入bar的引用,而不是传入foo,然后调用foo.bar。


闭包

           foo 是一个与Foo完全不同的野兽。Python里的任一个变量都是内存里对象的引用——对象之间都没什么不同。Foo.x,Foo.__init__ 和 Foo.bar这三个与foo.x,foo.__Init__, 和foo.bar不同,他们都指向不同的内存空间。

[python] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. print Foo.x is not foo.x  
  2. print Foo.__init__ is not foo.__init__  
  3. print Foo.bar is not foo.bar  

           Foo 和foo 是完全不相关的实体,它们只是碰巧在适当的时候相互引用对方。

阅读(866) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~