最近在学Python。以下是一个简短的总结。写这个东西的目的之一在于写代码的时候作参考,免于翻书查语法。不能保证我所写内容的正确性,仅供参考。
1 类的定义
class MyClass(object):
pass
这段代码定义了一个名为MyClass的类,类里没有任何内容。这个类是所谓“新式类”。如果去掉(object)写成
class MyClass:
pass
则是一个“经典类”。二者的区别尚不清楚。有人建议所有类都要用新式类
2 实例化一个对象
my_object = MyClass()
这句代码把MyClass类实例化为一个对象,对象名为my_object。
3 类的初始化(构造函数)
在Python中构造函数似乎是叫初始化。初始化可以不写,如果写的话则为如下形式。
class MyClass(object):
def __init__(self):
pass
其中init前后各有两个下划线。参数self是必须的,事实上Python类中所有方法的第一个参数都是self,详见下文。
如果采用这样的初始化方法,则实例化类的时候不需要传入参数,用如下语句即可实例化
my_object = MyClass()
此时初始化方法中的代码将被执行。
如果需要在构造函数中传入参数,则构造函数应定义如下
class MyClass(object):
def __init__(self, a, b, c):
pass
这个类的实例化则应采用如下方法
my_object = MyClass(xx, yy, zz)
此时初始化方法中的代码将被执行
4 类里的方法
Class MyClass(object):
def do_some_thing(self):
pass
def do_some_other_thing(self, a, b, c):
pass
这段代码定义了类MyClass,类里面有两个方法,分别为do_something()方法和do_some_other_thing()方法。两个方法的参数中,self是默认的参数。Python规定类里所有的方法的第一个参数都应该是self,且调用的时候不需要传入这个参数。也就是说,上面两个方法分别应该以如下方式调用
my_object = MyClass()
my_object.do_some_thing()
my_object.do_some_other_thing(xx, yy, zz)
其中xx,yy,zz是传入的三个参数,分别对应a,b,c。
5 类里的属性
这个还没有摸清,大致有如下几点。
5.1 总体感觉Python中的属性比较随意。测试表明,可以先把类实例化成对象,然后给对象赋属性。也就是说,属性并不一定写在类里。这事感觉略荒谬。
5.2 初步的测试表明,在类里,凡是以
self.some_thing = abc
格式使用的变量均被视为类的属性。并且属性使用的时候必须加上self前缀作为引导。
5.3 如果在类内、方法外定义一个变量,则这个变量是类的变量,而不是严格的属性。如果代码这么写
class MyClass(object):
name = “Jack Class”
则这个name是MyClass的变量,这个变量可以在不做实例化的情况下直接使用。换言之,代码可以这样写
print(MyClass.name)
而不必
my_object = MyClass()
print(my_object.name)
相应地,调用这个name的时候不需要加self前缀。
在类内、方法外定义的变量不允许加self前缀。即程序不允许这样写:
class MyClass(object):
self.name = “Jack Class”
上面这段程序会报错
5.4 在方法内使用的变量,如果不加self前缀,则就是一个普通的变量,作用域仅限于这个方法
5.5 综上,根据一个文档的建议,在Python中如果需要属性的话,尽量在初始化方法里把属性列出来初始化一下,至少能提高代码的可读性。另外,类的初始化方法如果是有参数的,那么这些参数通常是用于计算属性的。
6 类的继承
class ClassA(MyClass):
pass
这段代码定义了一个名为ClassA的类,这个类继承了MyClass类。Python也允许多重继承,例如
class ClassB(MyClass, YourClass):
pass
则ClassB继承了MyClass和YourClass两个父类
7 父类的调用
这个比较复杂,总体来说有以下几点。
7.1 在Python中,只有方法可以继承,属性不继承。
7.2 对于一个实例化的对象来说,可以直接调用父类的方法。如果子类有同名的方法,则会覆盖父类的相应方法。由于允许多重继承,实测表明,当两个父类有同名方法的时候,会以第一个父类为准。例如
class ClassB(MyClass, YourClass):
pass
如果MyClass和YourClass有同名方法,则ClassB继承MyClass中的方法。
7.3 如果要在子类代码里调用父类的方法,对于一个单继承的类,
class ClassA(MyClass):
pass
有两种方法可以调用父类。其一,直接用父类名,即在ClassA里写
MyClass.do_some_thing(self)
即可调用父类MyClass里的do_some_thing()方法。注意这里调用的时候需要写上self。其二,采用super()语法,即写作
super(ClassA, self).do_some_thing()
同样可以调用父类里的do_some_thing()方法。注意这里不需要写self。
7.4 在多重继承的情况下如果要在子类代码里调用父类的方法,例如
class ClassB(MyClass, YourClass):
pass
则方法一同样有效,即可以用
MyClass.do_some_thing(self)
YourClass.do_some_other_thing(self)
分别调用两个父类里的方法。方法二也可以运行,写作
super(ClassB, self).do_some_thing()
但是在多重继承的情况下super语法会有一些问题。从初步的情况来看,如果两个父类都有do_some_thing(),则貌似只会调用第一个父类的方法。从这个例子来说,就是只会调用MyClass的do_some_thing()方法。
7.5 在Python 中,属性是不直接继承的。不管是实例化后的对象,还是在子类代码里调用父类,均不能直接识别父类的属性。这很可能是因为在Python中,属性都是在方法里定义的。子类在继承父类的过程中并没有运行父类的任何方法(父类的初始化方法也不会被运行),因此相应的属性都没有被定义。
测试表明,如果父类在某个方法里定义了一个属性
class MyClass(object):
def do_some_thing(self):
self.dream = “I have a dream”
print(self.dream)
又有子类
class ClassA(MyClass):
pass
那么ClassA如果要使用父类的属性dream,就必须先运行父类的方法do_some_thing()。运行这个方法后,父类MyClass的属性dream就会被子类ClassA所继承。不管是实例化后的对象还是在子类代码里调用父类的属性,如果想要使用父类属性的话,必须先运行相应的方法。
这提醒我们,在Python中如果要使用“属性”这种面向对象特性的话,应当写一个初始化属性的方法。这样在类被继承的时候,子类运行一下父类的属性初始化方法,就可以继承到父类的所有属性了。类的初始化方法好像是理想的属性初始化位置。
此外测试还表明,如果在类内、方法外定义一个变量,那么这个变量是可以被继承的。但如5.3所说,这样的变量不是严格的属性。
7.6 关于super()
前文提到了super方法。网上的资料没有看懂。这个super存在的意义似乎是在复杂的多重继承情况下,如果要调用父类的方法,可能会出现一些方法的运行次序问题,有一些方法可能会被运行多次。更深入的解释没看明白。super通过一些机制来解决这些问题。但是实测表明,super不能解决多个父类的同名方法需要依次运行这个问题。文档建议仅在类的初始化方法里使用super来运行父类的初始化方法。在没有弄清楚的情况下,应当避免使用super,并避免使用复杂的继承关系。
加载中,请稍候......