前言:
数据层面的封装是创建列表,代码层面的封装是创建函数,对象将数据和代码都封装了。
对象=属性+方法
对象的特征称为属性,行为称为方法
创建类
class Turtle:
#Python的类名为大写字母开头
color = 'green'
legs = 4
def climb(self): #类的方法就是函数
print('go')
以上代码定义对象的属性和行为,即创建了类。通过类创建对象,将类实例化,则创建出的对象称为类的一个实例,也称实例对象。
类的实例化示例
>>>tt = Turtle()
类的实例化过程类似于调用函数,所以要求类名大写字母开头,函数小写字母,易于区分。赋值操作即将创建好的实例对象赋值给一个变量,否则对象无法使用(没有任何引用指向该实例),会被当作垃圾回收。
调用对象的方法
使用点操作符即可
>>>tt.climb()
go
self
对象的方法都带有self参数,self相当于C++的this指针,self理解为能唯一标识类所创建的无数对象中某特定对象的存在。
Python的self同理,当某类实例化出无数对象时,调用其中某对象的方法,对象会将自身的引用作为第一个参数传入该方法,Python借此识别哪个对象的方法。
>>>class Ball:
def setName(self,name):
self.name = name
def kick(self):
print("you kick %s" % self.name)
>>>a = Ball()
>>>a.setName("me")
>>>b = Ball()
>>>b.setName("him")
>>>a.kick()
you kick me
>>>b.kick()
you kick him
构造函数
Python的特殊方法总是被双下划线包围,如_ _init_ _()方法,实例化对象时可以传入参数,这些参数自动传入_ _init_ _()方法中,达到初始化对象的目的。
该方法称为构造方法,会在类实例化一个对象时自动调用,利用此特性,使用该方法达到初始化对象的目的。
>>>class Potato:
def _ _init_ _(self,name):
self.name = name
def kick(self):
print("you kick %s" % self.name)>>>p = Potato("potato")
>>>p.kick()
you kick potato
公有和私有
公有和私有用于控制访问权限,但Python并没有public和private关键字。默认对象的属性和方法都是公开的,通过点操作符进行访问。
>>>class Person:
name = "Jack"
>>>p = Person()
>>>p.name
'Jack'
Python采用名字改编的方式来定义私有变量,即在变量或函数名前加上两个下划线"_ _"
>>>class Person:
_ _name = "Jack"
>>>p = Person()
>>>p._ _name #error
在外部将变量名隐藏起来,如果要访问,就需要从内部进行
>>>class Person:
def _ _init_ _(self,name):
self._ _name = name
def getName(self):
return self._ _name
>>>p = Person("Jack Ma")
>>>p._ _name #error
>>>p.getName()
'Jack Ma'
仔细分析名字改编,其实Python只是把双下横线开头的变量进行改名,实际上等效于在外部使用"_类名_ _变量名"即可访问双下横线开头的私有变量
>>>p._Person_ _name
'Jack Ma'
(注:Python的私有机制是伪私有,类是没有权限控制的,所有变量都可被外部调用)
继承
语法
class 类名(被继承的类):
被继承的类称为基类,父类或超类,继承者称为子类,子类继承父类的任何属性和方法
>>>class Parent:
def hello(self):
print("haha")
>>>class Child(Parent):
pass
>>>p = Parent()
>>>p.hello()
haha
>>>c = Child()
>>>c.hello()
haha
若子类定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性,包括构造方法:
>>>class Child(Parent):
def hello(self):
print("xixi")
>>>c = Child()
>>>c.hello()
xixi
import random as r
class Fish:
def _ _init_ _(self):
self.x = r.randint(0,10)
self.y = r.randint(0,10)
def move(self):
self.x -= 1
print("my position:",self.x,self.y)
class Goldfish(Fish):
pass
class Shark(Fish):
def _ _init_ _(self):
self.hungry = True
def eat(self):
if self.hungry:
print("eat")
self.hungry = False
else:
print("Too Much")
>>>fish = Fish()
>>>fish.move()
my positon:5 10
>>>goldfish = Fish()
my position:8 9
>>>goldfish.move()
my position:7 9
>>>goldfish.move()
my position:6 9>>>shark = Shark()
>>>shark.eat()
eat
>>>shark.eat()
Too Much
>>>shark.move() #error
虽然Shark继承了Fish,但其构造函数重写了父类的构造函数,而新的构造函数没有初始化x和y坐标,调用move()方法自然报错,所以重写构造方法时应先调用Fish类(基类)的构造函数,两种实现方法如下
调用未绑定的父类方法
class Shark(Fish):
def _ _init_ _(self):
Fish._ _init_ _(self)
#该self是子类Shark的实例对象
self.hungry = True
未绑定指的是不需要绑定父类的实例对象,而使用子类的实例对象代替
使用super函数
super()函数能自动找到父类的方法,并传入self参数
class Shark(Fish):
def _ _init_ _(self):
super()._ _init_ _()
self.hungry = True
super在于不需要给出基类名字,自动找到基类及其对应方法,好处在于当改变基类时只要改变class语句中的父类即可,而不用在大量代码中修改所有被继承的方法
多重继承
不推荐使用
class类名(父类1,父类2,...):
>>>class C(Base1,Base2):
pass
组合
直接把需要的类放进去实例化
class Turtle:
def _ _init_ _(self,x):
self.num = x
class Fish:
def _ _init_ _(self,x):
self.num = x
class Pool:
def _ _init_ _(self,x,y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def print_num(self):
print("总共有乌龟%d只,小鱼%d条"%(self.turtle.num,self.fish.num))
>>>pool = Pool(1,10)
>>>pool.print_num()
总共有乌龟1只,小鱼10条