概述
1. python中的self用法总结
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
__init__
方法的第一参数永远是self,表示创建的类实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。- 有了
__init__
方法,在创建实例的时候,就不能传入空的参数了,必须传入与init方法匹配的参数,但self不需要传,Python解释器会自己把实例变量传进去。 - 和普通数相比,在类中定义函数只有一点不同,就是第一参数永远是类的本身实例变量self,并且调用时,不用传递该参数。除此之外,类的方法(函数)和普通函数没啥区别,你既可以用默认参数、可变参数或者关键字参数(*args是可变参数,args接收的是一个tuple,**kw是关键字参数,kw接收的是一个dict)
- 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
- 需要注意的是,在Python中,变量名类似xxx的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用
__name__、__score__
这样的变量名 - 有些时候,你会看到以一个下划线开头的实例变量名,比如
_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
下面是self的一些详细用法:
(1)self代表类的实例,而非类
In [19]: class Test:
...:
def ppr(self):
...:
print(self)
...:
print(self.__class__)
...:
In [20]: t = Test()
In [21]: t.ppr()
<__main__.Test instance at 0x000000000D3D4E88>
__main__.Test
self代表的是类的实例。而self.__class__
则指向类。
注意:把self换成this,结果也一样,但Python中最好用约定俗成的self。
(2)self可以不写吗
在Python解释器的内部,当我们调用t.ppr()时,实际上Python解释成Test.ppr(t),也就是把self替换成了类的实例。
In [22]: class Test:
...:
def ppr():
...:
print(self)
...:
In [23]: t=Test()
In [24]: t.ppr()
Traceback (most recent call last):
File "<ipython-input-24-178e9dc94925>", line 1, in <module>
t.ppr()
TypeError: ppr() takes no arguments (1 given)
这里实际上已经部分说明了self在定义时不可以省略。
当然,如果我们的定义和调用时均不传类实例是可以的,这就是类方法。
>>> class Test:
def ppr():
print(__class__)
>>> Test.ppr()
<class '__main__.Test'>
>>>
但这里要注意下:对于python 2.7版本来说会报错,必须先创建对象。
In [31]: class Test:
...:
def ppr():
...:
print('i am here')
...:
In [32]: Test.ppr()
Traceback (most recent call last):
File "<ipython-input-32-f7a5b2f2ccd9>", line 1, in <module>
Test.ppr()
TypeError: unbound method ppr() must be called with Test instance as first argument (got nothing instead)
(3)在继承时,传入的是哪个实例,就是那个传入的实例,而不是指定义了self的类的实例
>>> class Parent:
def pprt(self):print(self)
>>> class Parent:
def pprt(self):
print(self)
>>> class Child(Parent):
def cprt(self):
print(self)
>>> c=Child()
>>> c.cprt()
<__main__.Child object at 0x00000244B8769860>
>>> c.pprt()
<__main__.Child object at 0x00000244B8769860>
>>> p = Parent()
>>> p.pprt()
<__main__.Parent object at 0x00000244B8769A58>
>>>
(4)在描述符类中,self指的是描述符类的实例
class Desc2:
def __get__(self, ins, cls):
print ('self in Desc: %s ' % self )
print (self, ins, cls)
class Test2:
x = Desc2() # 而该属性是描述符属性,为Desc类的实例
def prt(self):
print ('self in Test: %s' % self)
t2 = Test2()
t2.prt()
t2.x # 这里是调用类属性
Test2.x # 注意和上面一行的区别
运行结果:
self in Test: <__main__.Test2 object at 0x00000239CE269128>
self in Desc: <__main__.Desc2 object at 0x00000239CE2690B8>
<__main__.Desc2 object at 0x00000239CE2690B8> <__main__.Test2 object at 0x00000239CE269128> <class '__main__.Test2'>
self in Desc: <__main__.Desc2 object at 0x00000239CE2690B8>
<__main__.Desc2 object at 0x00000239CE2690B8> None <class '__main__.Test2'>
由于在很多时候描述符类中仍然需要知道调用该描述符的实例是谁,所以在描述符类中存在第二个参数ins,用来表示调用它的类实例,所以t.x时可以看到第三行中的运行结果中第二项为<__main__.Test2 object at 0x00000239CE269128>
2. 描述符__set__
和__get__
的用法
可以先参考下:https://blog.csdn.net/lilong117194/article/details/80111803
上面有疑问的地方应该是__get__(self, ins, cls)
,下面总结一下:
首先说下python中存在的几种方法:对象方法、静态方法、类方法等,归属权分别为obj、cls、cls
其实可以从他们的参数中就可以看的出来,静态方法使用@staticmethod来修饰,可以通过类或类的实例对象来调用而已。
class A(object):
def foo1(self):
print ("Hello",self)
@staticmethod
def foo2():
print ("hello")
@classmethod
def foo3(cls):
print ("hello",cls)
# cls就是类A本身
a = A()
a.foo1()
A.foo1(a)
#这里传入实例a,相当于普通方法的self
A.foo2()
#这里,由于静态方法没有参数,故可以不传东西
A.foo3()
#这里,由于是类方法,因此,它的第一个参数为类本身。
print (A)
#可以看到,直接输入A,与上面那种调用返回同样的信息
结果:
Hello <__main__.A object at 0x0000016D1774B860>
Hello <__main__.A object at 0x0000016D1774B860>
hello
hello <class '__main__.A'>
<class '__main__.A'>
Python中,对象的方法也是也可以认为是属性,所以下面所说的属性包含方法在内。
看一个实例:
class T(object):
name = 'name'
def hello(self):
print ("hello")
t = T()
print ('dir(t):',dir(t))
print ('t.__class__:',t.__class__)
print ('t.__dict__:',t.__dict__)
print ('T.__dict__:',dict(T.__dict__)) # T.__dict__并没有直接返回dict对象,这里转换为字典
print ('t.name:',t.name)
print ('T.name:',T.name)
print ('T.dict[‘name’]:',T.__dict__['name'])
print ('T.hello:',T.hello) # 是个unbound method
print ("T.__dict__['hello']:",T.__dict__['hello']) # 是个function
print ('t.hello:',t.hello) # 是个bound method
运行结果:
dir(t): ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'hello', 'name']
t.__class__: <class '__main__.T'>
t.__dict__: {}
T.__dict__: {'name': 'name', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'T' objects>, '__module__': '__main__', 'hello': <function T.hello at 0x000002BB9572B400>, '__dict__': <attribute '__dict__' of 'T' objects>}
t.name: name
T.name: name
T.dict[‘name’]: name
T.hello: <function T.hello at 0x000002BB9572B400>
T.__dict__['hello']: <function T.hello at 0x000002BB9572B400>
t.hello: <bound method T.hello of <__main__.T object at 0x000002BB97B49860>>
注意:
- 有些内建类型,如list和string,它们没有dict属性,随意没办法在它们上面附加自定义属性
t.__dict__: {}
:到现在为止t.dict是一个空的字典,因为我们并没有在t上自定义任何属性,它的有效属性hello和name都是从T得到的。
t.name
的查找过程:
- 首先,Python判断name属性是否是个自动产生的属性,如果是自动产生的属性,就按特别的方法找到这个属性,当然,这里的name不是自动产生的属性,而是我们自己定义的,Python于是到t的dict中寻找。还是没找到。
- 接着,Python找到了t所属的类T,搜索T.dict,找到后返回name的值:字符串‘name’。如果在T.dict中还没有找到,Python会接着到T的父类(如果T有父类的话)的dict中继续查找。
t.hello
:
- 方法在类的dict中是以函数的形式存在的
- 关于unbound和bound可以这样:方法是要从实例调用的,如果从类中访问,如T.hello,hello没有和任何实例发生联系,也就是没绑定(unbound)到任何实例上,所以是个unbound,对t.hello的访问方式,hello和t发生了联系,因此是bound。
下面再看一个例子:
class Descriptor(object):
def __get__(self, obj, type=None):
return 'get', self, obj, type
def __set__(self, obj, val):
print ('set', self, obj, val)
def __delete__(self, obj):
print ('delete', self, obj)
class T(object):
# d是T的类属性,作为Descriptor的实例,它有get等方法,是一个描述符
d = Descriptor()
t = T()# T是一个类,t是它的一个实例,d是T的一个descriptor属性
print (' t.d:', t.d) # t.d,返回的实际是d.__get__(t, T)
print (' T.d:', T.d) # T.d,返回的实际是d.__get__(None, T),所以obj的位置为None
t.d = 'hello' # 在实例上对descriptor设置值,在__set__方法中print语句输出的。
print (' t.d:', t.d) # 可见,调用了Python调用了__set__方法,并没有改变t.d的值
T.d = 'hello' # 没有调用__set__方法
print (' T.d:', T.d)
# t.d的值也变了,这可以理解,按我们上面说的属性查找策略,t.d是从T.__dict__中得到的T.__dict__['d']的值是'hello',t.d当然也是'hello'
print (' t.d:', t.d)
运行结果:
t.d: ('get', <__main__.Descriptor object at 0x000001F403DF9860>, <__main__.T object at 0x000001F403E5B6D8>, <class '__main__.T'>)
T.d: ('get', <__main__.Descriptor object at 0x000001F403DF9860>, None, <class '__main__.T'>)
set <__main__.Descriptor object at 0x000001F403DF9860> <__main__.T object at 0x000001F403E5B6D8> hello
t.d: ('get', <__main__.Descriptor object at 0x000001F403DF9860>, <__main__.T object at 0x000001F403E5B6D8>, <class '__main__.T'>)
T.d: hello
t.d: hello
这里需要说明几点:
- self当然不用说,指的是当前Descriptor类的实例。obj值拥有属性的对象。这应该不难理解,前面已经说了,descriptor是对象的稍微有点特殊的属性,这里的obj就是拥有它的对象。要注意的是,如果是直接用类访问descriptor(descriptor是个属性,直接用类访问descriptor就是直接用类访问类的属性),obj是None,此时type就是类本身。
- 读取属性时,如T.d,返回的是d.get(None, T)的结果,t.d返回的是d.get(t, T)的结果。
- 设置属性时,t.d = value,实际上调用d.set(t, value)
- T.d = value,这是真正的赋值,T.d的值从此变成value。删除属性和设置属性类似。
data descriptor和non-data descriptor:
象上面的d,同时具有get和set方法,这样的descriptor叫做data descriptor,如果只有get方法,则叫做non-data descriptor。容易想到,由于non-data descriptor没有set方法,所以在通过实例对属性赋值时,会直接赋值。
属性查找策略:
1.如果attr是一个Python自动产生的属性,找到!(优先级非常高!)
2.查找obj.class.dict,如果attr存在并且是data descriptor,返回data descriptor的get方法的结果,如果没有继续在obj.class的父类以及祖先类中寻找data descriptor
3.在obj.dict中查找,这一步分两种情况,第一种情况是obj是一个普通实例,找到就直接返回,找不到进行下一步。第二种情况是obj是一个类,依次在obj和它的父类、祖先类的dict中查找,如果找到一个descriptor就返回descriptor的get方法的结果,否则直接返回attr。如果没有找到,进行下一步。
4.在obj.class.dict中查找,如果找到了一个descriptor(插一句:这里的descriptor一定是non-data descriptor,如果它是data descriptor,第二步就找到它了)descriptor的get方法的结果。如果找到一个普通属性,直接返回属性值。如果没找到,进行下一步。
5.很不幸,Python终于受不了。在这一步,它raise AttributeError
到这里已经差不多了,但还是有些不是理解的很透彻,以后慢慢再学习。
参考:https://blog.csdn.net/u014015972/article/details/51168268
最后
以上就是高贵服饰为你收集整理的python中的self&描述符__set__和__get__&简单总结的全部内容,希望文章能够帮你解决python中的self&描述符__set__和__get__&简单总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复