写一个简单的类
class Lion(object):
def __init__(self,color,sound):
self.color=color
self.sound=sound
类名后面打括号加object
的意思是它是一个基类,不带参数或者连括号都不带默认也是这个意思,如果它是某个类的子类,那么不仅要带括号,括号中还要写上父类名。
你可以将这个方法类比为C++里面的构造函数,实际上它就是一个构造函数。
新建类实例时会被自动调用,为属性赋值,如果不特别声明,它只会被初始化一遍,往后不管将实例使用几遍,__init__()
方法不会再被自动调用。
self是__init__()方法的第一个参数,必须存在。它指向类实例的地址,是实例得以存在的原因。当然,如果后面带上__class__
,它便会指向当前类。实际上,哪怕__init()__
方法的第一个参数不叫self也行,但必须有一个额外参数作为指针存在。譬如我们将上面那个类写成下面这样也行
class Lion(object):
def __init__(xxx,color,sound):
xxx.color=color
xxx.sound=sound
那我上面构造的那个类来说,它就有两个属性——color
,sound
。它们分别代表狮子的颜色和叫声(lion:狮子)。当然,狮子不仅有这两方面的特点,譬如它几岁,它住在哪里,这些都可以作为属性添加。
类比C++,它的属性也有三种
类型 | 特点 | 格式 |
---|---|---|
公有属性 | 允许随意访问 | self.name |
受保护属性 | 允许随意访问,但是不建议直接访问 | self._name |
私有属性 | 拒绝访问 | sel.__name |
说明:公有属性以字母开头,受保护属性以单下划线开头,私有属性以双下划线开头。后面讲到的三种类方法也已这种方式定义。
下面我们来看
class Sample(object):
def __init__(self):
self.a=1
self._b=2
self.__c=3
shili=Sample()
print(shili.a)
print(shili._b)
output
1
2
而这段代码
class Sample(object):
...
...
...
print(shili.__c)
output
1
2
Traceback (most recent call last):
File "test.py", line 24, in
print(shili.__c)
AttributeError: 'Sample' object has no attribute '__c'
可以看出,受保护属性虽不建议直接访问但的确是可以直接访问的,而私有属性压根儿找不着。那么以怎样的方式去使用受保护属性和私有属性呢?在类方法中我会讲到。
对待类方法,我们仍然像对待一般方法那样,使用def关键字进行定义,但形参和一般方法有所区别。就像我们的__init__()
方法那样,类方法也需有特定的首位形参,与__init__()
方法的相同。
看下面这段代码
class Sample(object):
...
...
...
def fun(self):
pass
这即为类方法的定义了。
类方法访问属性同样须使用self
关键字,假如你给__init__()
的第一个形参叫做xxx
那么就用xxx
来访问。总之__init__()
方法的第一个参数你不管传什么它都会立刻被提升为关键字。
看下面这段代码
class Sample(object):
...
...
...
def fun(self):
print(self.a)
譬如说你想打印a属性,那么应该这样给print()
传参。
最特殊的类方法毋庸置疑当然是__init__()
。那还有些什么地位比较超然的类方法呢?这里列举一些
方法名 | 方法释义 |
---|---|
_del_ | 析构函数,释放对象时使用 |
_repr_ | 打印,转换 |
_setitem_ | 按照索引赋值 |
_getitem_ | 按照索引获取值 |
_len_ | 获得长度 |
_cmp_ | 比较运算 |
_call_ | 函数调用 |
_add_ | 加运算 |
_sub_ | 减运算 |
_mul_ | 乘运算 |
_truediv_ | 除运算 |
_mod_ | 求余运算 |
_pow_ | 乘方 |
类方法由类实例来调用,类实例通过.
来调用类方法
如何产生一个类实例?看下面这段代码
class Sample(object):
...
...
...
shili=Sample()
类实例由此产生,第五行的Sample
是否带参数依__init__
方法而定。根据上面那个方法,我们是不需要给参数的。
调用类方法访问受保护属性以及私有属性
对于受保护以及私属性,我们一般在类中提供接口进行访问,无论是取值还是修改。看下面这段代码
class Sample(object):
"""docstring for Sample"""
def __init__(self):
...
self._b=2
self.__c=3
...
def set_b(self,_b):
self._b=_b
def get_b(self):
return self._b
def set_c(self,__c):
self.__c=__c
def get_c(self):
return self.__c
if __name__=="__main__":
shili=Sample()
print(shili.get_b(),shili.get_c())
shili.set_b(5)
shili.set_c(6)
print(shili.get_b(),shili.get_c())
output
2 3
5 6
### 属性分类的意义
根据自己的意愿和需要对属性进行定制
实例属性在__init__
方法中定义,类初始化时产生,也就是说它只对实例可见,那我们怎么给类定义属性呢?看这段代码
class Sample(object):
e=0
_e=1
__e=2
def __init__(self):
...
...
__init__
方法上面初始化的那些变量就是类属性。实力属性与类属性的区别在于,实例属性由实例独享,类属性由这个类的所有实例共享。两类属性的分级和级别所对应的访问限制是一样的。
被继承的类叫基类、父类或者超类,也有一种比较小众的叫法:根类。继承而来的类叫做子类。
在写法上与基类上没有太大区别,你只需要注意在类名后面的括号里写上基类名就行了。参考下面写法
class Sample(object):
#基类
...
class Son(Sample):
#子类
...
如果基类在某个模块当中,假定这个模块叫做Xxx
,那么子类这么写
class Son(Xxx.Sample):
...
在继承一个类的同时,我们也会继承它的属性和方法,对于属性和方法,我们可以选择覆写。进行重写之后再有子类实例调用该方法或属性则直接从子类中调用。见下面代码
class Sample(object):
"""docstring for Sample"""
e=0
...
def __init__(self):
...
self._b=2
...
def get_b(self):
return self._b
class Son(Sample):
e=1
def get_e(self):
return self.e
if __name__=="__main__":
shili=Son()
print(shili.get_e())
print(shili.get_b())
output
1
2
在上面这段代码中,子类Son覆写了基类的e属性,继承了基类的构造方法以及get_b
方法,并由自己定义了get_e
方法。调用get_b
方法对没有被覆写的_b
进行了打印,调用get_e
方法打印了被覆写的e
属性。可以看出的是,子类不仅能够自由使用父类的方法,还能自由覆写父类的属性。可是不是什么属性都能被覆写呢?请看代码
class Sample(object):
"""docstring for Sample"""
e=0
...
def __init__(self):
...
self._b=2
...
def get_b(self):
return self._b
class Son(Sample):
_b=1
if __name__=="__main__":
shili=Son()
print(shili.get_b())
output
2
我们明明已经在子类当中将_b修改成了1,为什么打印出来还是父类中的2呢?请看代码
class Sample(object):
"""docstring for Sample"""
e=0
...
def __init__(self):
...
self._b=2
...
def get_b(self):
return self._b
class Son(Sample):
def __init__(self):
self._b=1
if __name__=="__main__":
shili=Son()
print(shili.get_b())
output
1
在子类中重写了__init__
方法之后覆写就生效了。所以,如果你要覆写基类的实例属性,那么同时你也应该覆写__init__
方法。因为如果子类里面没有__init__
方法,那么Python就会调用父类的__init__
方法对实例属性进行初始化,子类实例属性便无法覆盖基类了。
这引发了一个思考,如果覆写的是类属性而不是实例属性,就万事大吉了吗?看代码
class Sample(object):
"""docstring for Sample"""
e=0
_e=1
__e=2
...
def __init__(self):
...
def get_1(self):
return self.e
def get_2(self):
return self._e
def get_3(self):
return self.__e
class Son(Sample):
e=4
_e=5
__e=6
if __name__=="__main__":
shili=Son()
print(shili.get_1())
print(shili.get_2())
print(shili.get_3())
output
4
5
2
可见e和_e都被覆写了,但是__e没有,还是那个私有属性的问题,之前说了它不可以在类外被访问,这里再一次验证。
这样一来又引发了一个思考,基类所有方法都可以被覆写吗?看代码
class Sample(object):
...
def __init__(self):
...
def one(self):
return "我是公有方法"
def _one(self):
return "我是受保护方法"
def __one(self):
return "我是私有方法"
class Son(Sample):
pass
if __name__=="__main__":
shili=Son()
print(shili.one())
print(shili._one())
print(shili.__one())
output
我是公有方法
我是受保护方法
Traceback (most recent call last):
File "test.py", line 65, in
print(shili.__one())
AttributeError: 'Son' object has no attribute '__one'
看,方法的分类也是对标类属性的,私有方法一样不能让子类访问。可能你要问了,公有方法和受保护方法的效果是一样的啊,二者之间的划分有什么意义呢?原则上,公有方法可以跨模块调用,而受保护方法不可以。但根据枫枫的实操,你要跨模块调用受保护方法也可以。
如果子类实例想要调用父类中某个被覆写的方法怎么办?看代码
class Sample(object):
...
def __init__(self):
...
def Print(self):
print("我是父类")
class Son(Sample):
def Print(self):
print("我是子类")
if __name__=="__main__":
shili=Son()
shili.Print()
super(Son,shili).Print()
output
我是子类
我是父类
super
方法可以被用来调用父类。传递子类名以及子类实例就可以访问父类被覆写的方法了。
多继承不是A->B->C
的关系,而是A->C,B->C
的关系,即多个父类对应单个子类。形如
class One:
pass
class Two:
pass
class Three(One,Two):
pass
多继承不会直接继承父类中的构造函数,因为可能会有多个构造函数,你需要在子类中进行覆写,如果不想覆盖父类的构造函数,至少要多个构造函数进行整合。示例如下
class One(object):
def __init__(self,a):
self.a=a
class Two(object):
def __init__(self,b):
self.b=b
class Three(One,Two):
def __init__(self,a,b):
One.__init__(self,a)
Two.__init__(self,b)
def get_a(self):
return self.a
def get_b(self):
return self.b
if __name__=="__main__":
shili=Three(1,2)
print(shili.get_a())
print(shili.get_b())
output:
1
2
如果多个父类中有同名函数怎么办?默认调用括号中排序靠前的类的方法。示例如下
class One(object):
def __init__(self,a):
self.a=a
def Print(self):
print("I am One.")
class Two(object):
def __init__(self,b):
self.b=b
def Print(self):
print("I am Two.")
class Three(One,Two):
def __init__(self,a,b):
One.__init__(self,a)
Two.__init__(self,b)
...
if __name__=="__main__":
shili=Three(1,2)
shili.Print()
output
I am One.