一、定义
1、语法
class dog(object): #用class定义类
"dog class" #对类的说明
def __init__(self,name): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
def bark(self):
print("dog 汪汪。。。",self.name) #类方法
def bite(self):
print("dog 咬。。。",self.name)
d = dog('Hally')
d.bark()
d.bite()
2、self关键字
self 这个关键字相当于实例化对象本身(self相当于d),在实例化过程中,把自己传进去了
d = dog('Hally') 相当于:d = dog(d,'Hally')
d.bite()相当于d.bite(d)
self.name=name 相当于 d.name=name
3、函数__init__()
在类中__init__()函数叫构造函数,又叫构造方法,也可以叫初始化函数。
它的作用就是初始化实例时,初始化传入实例的的默认值。
如果不写__init__(),就会调用的默认为空的__init__()
即这个方法不管写不写,都会调用,而且,一旦实例化就会调用。
class dog(object): #用class定义类
"dog class" #对类的说明
def __init__(self,name): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
def eat(self,food):
print("the dog name is {0},it likes food is {1}".format(self.name,food)) #类方法
d = dog('Hally')
d.eat("bone")
#结果
the dog name is Hally,it likes food is bone
其实self,就是实例本身!你实例化时python会自动把这个实例本身通过self参数传进去。
4、小结
- 定义类(class dog(object))-> 实例化(d = dog()) -> 实例化对象(d)
- __init__()构造函数
- self.name = name 被称为属性、成员变量、字段
- def eat(self) 被称为方法、动态属性
二、访问类属性
1、成员变量和参数
class dog(object): #用class定义类
"dog class" #对类的说明
def __init__(self,name): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
def bark(self):
print("{0} 汪汪。。。".format(self.name)) #类方法
def eat(self,food):
print("the dog name is {0},it likes food is {1}".format(self.name,food)) #类方法
d = dog('Hally') #实例化对象
d.bark() #调用bark()方法
d.eat("bone") #调用eat()方法
结果:
Hally 汪汪。。。
the dog name is Hally,it likes food is bone
有两个问题:
①为什么eat方法里面会传入一个food的参数名?而这个为什么不能再其他方法里面用呢?而self.name就可以呢?
因为food它只是作为eat方法的一个参数,不是类的成员变量,只能在自己的方法里面使用,而self.name是类的成员变量,它是类的属性,所以可以被调用。
②为什么类的每一个方法第一个参数都是self呢?
是因为类在实例化的时候,需要把实例对象本身传进来,好去调用对象本身的属性和方法。
2 、修改对象的属性
class dog(object): #用class定义类
"dog class" #对类的说明
def __init__(self,name,age): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
self.age = age
def bark(self,update_age):
self.age = update_age
print("{0} 汪汪。。。,{1}岁".format(self.name,update_age)) #类方法
d = dog('Hally',16) #实例化对象
d.bark(18) #第1次在类的方法中修改
print(d.age)
d.bark(30)
print(d.age) #第2次在类的方法中修改
#结果
Hally 汪汪。。。,18岁
18
Hally 汪汪。。。,30岁
30
在类的方法中或者类的外面都可以修改类的属性
三、定义私有属性
可以访问类的属性,也可以修改类的属性,但是类属性一旦被定义成私有的,对外是不可以被访问的,也是不可以随意被改变的。
1、 定义私有属性
class dog(object): #用class定义类
"dog class" #对类的说明
def __init__(self,name,age): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
self.age = age
self.__sex = 'girl' #定义私有属性__sex
def bark(self,update_age):
self.age = update_age
print("{0} 汪汪。。。,{1}岁".format(self.name,update_age)) #类方法
d = dog('Hally',16) #实例化对象
print(d.__sex) #访问私有属性__sex
#结果
Traceback (most recent call last):
File "/Users/bianbian/PycharmProjects/test/test18.py", line 12, in <module>
print(d.__sex) #访问私有属性__sex
AttributeError: 'dog' object has no attribute '__sex'
可以看出,私有属性是不可访问,更不可以修改。但是我们就想访问,但是不去修改,我们怎么办呢?
2、访问私有属性
虽然我们外部不能访问私有属性,但是我们在类内部可以访问私有属性,所以我们用如下方法,访问私有属性
(1)get方法访问私有属性
class dog(object): #用class定义类
"dog class" #对类的说明
def __init__(self,name,age): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
self.age = age
self.__sex = 'girl' #定义私有属性__sex
def get_sex(self): #定义get方法
return self.__sex #返回私有属性sex值
d = dog('Hally',16) #实例化对象
d_sex = d.get_sex() #调用实例对象d的get_sex()方法获取私有属性sex
print(d_sex) #访问私有属性__sex
#结果
girl
这种方法只能访问私有属性,但是不能更改私有属性
(2)强制访问私有属性
下面这种方法就更为暴力,可以访问也可以修改,就是:对象名._类名__属性名
class dog(object): #用class定义类
"dog class" #对类的说明
def __init__(self,name,age): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
self.age = age
self.__sex = 'girl' #定义私有属性__sex
d = dog('Hally',16) #实例化对象
print(d._dog__sex) #访问私有属性sex
d._dog__sex='boy' #修改私有属性sex
print(d._dog__sex) #打印修改后的值
#结果
girl
boy
3、总结
- 定义私有属性:self.__private_attr_name = private_attr_name
- 强制访问私有属性:对象名._类名__属性名(d._dog__sex)
- 对外提供只读接口访问:
def get_sex(self):
return self.__sex
四、类的公有属性
定义:指的是所属这个类的所有对象,都可以访问的属性,叫做公有属性。
1、定义
在类中直接定义的,可以提供这个类所属的所有对象都可以访问的属性
class dog(object): #用class定义类
"dog class" #对类的说明
age = 18 #定义公有属性nationality
def __init__(self,name): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
self.__sex = 'girl' #定义私有属性__sex
d1 = dog('Hally') #实例化对象1
d2 = dog('Kite') #实例化对象2
print(d1.age,d2.age) #所有的成员变量都可以访问
print(d1.name,d2.name)
#结果
18 18
Hally Kite
所有的成员变量都可以访问age,且值相同。
d1.name,d2.name也可以访问,也可以修改, 为什么不能叫公有属性
上面的name是每个对象的属性,并不是共享,而是独立的 。
2、 类访问公有属性
公有属性不仅可以通过所属类的所有对象访问,还可以通过类本身直接访问和修改
class dog(object): #用class定义类
"dog class" #对类的说明
age = 18 #定义公有属性nationality
def __init__(self,name): #构造函数或者是构造方法,也可以叫初始化方法
self.name = name
self.__sex = 'girl' #定义私有属性__sex
d1 = dog('Hally') #实例化对象1
d2 = dog('Kite') #实例化对象2
print(d1.age,d2.age) #访问公有属性
dog.age = 16 #修改公有属性
print(d1.name,d2.name)
print(d1.age,d2.age) #访问修改后的公有属性
#结果
18 18
Hally Kite
16 16
3、 公有属性特性
公有属性不是私有属性,它提供所有对象访问和修改,那我们其中有一个对象正在修改公共属性发生什么变化呢?或者类本身直接修改了公共属性,又放生什么变化呢?
class dog(object): #用class定义类
"dog class" #对类的说明
age = 18 #定义公有属性nationality
def __init__(self,name):
self.name = name
self.__sex = 'girl' #定义私有属性__sex
d1 = dog('Hally') #实例化对象1
d2 = dog('Kite') #实例化对象2
print("————————aboriginal value————————")
print(d1.age,d2.age) #访问公有属性
print("————————brfore change__________")
d1.age = 16 #对象的d1修改公共属性de值
print(d1.age,d2.age)
print("_______after change ___________")
dog.age = 20
print(d1.age,d2.age) #访问修改后的公有属性
#结果
————————aboriginal value————————
18 18
————————brfore change__________
16 18
#d1对象的公共属性被修改了
_______after change ___________
16 20 #d1对象的公共属性值没有随着类本身的公共属性值修改而修改
(1)对象d1去访问age属性时,如果在成员属性中找不到,就会找公共属性,也就是说自己的属性找不到就去找父亲的属性
(2)d1.age=16 相当于在自己对象内部又重新创建了一个新的局部变量,这个局部变量已经脱离了class本身,跟这个类已经没有关系了,只是名字一样而已,如果不改,还是找全局的。
4、公共属性小结
公有属性只有一个,而且是在没有定义对象的时候,就已经存在,那么类中的方法不是每调用一次,就会生成一个。
即,实例化后调用eat方法,只是拿着这块内存中的数据去调公有的eat方法而已,并不是有多个。
五、析构函数
定义:在实例销毁的时候调用的函数
class dog(object): #用class定义类
"dog class" #对类的说明
age = 18 #定义公有属性nationality
def __init__(self,name):
self.name = name
def eat(self,food):
print("Dog {0} is eating {1}".format(self.name,food))
def __del__(self): #定义析构函数
print("-------del-----")
d =dog('Mike')
d.eat("bone")
del d
import time
time.sleep(5)
#结果
Dog Mike is eating bone
-------del-----
①析构函数的调用
每一个对象都是一个应用,就像每一个房间都有门牌号一样, 只要这个对象的引用被清空时,就会自动执行,就像上面的del d,其实python中有自动垃圾回收机制,会定时去的去回收一些被清空的应用,而析构函数就是在引用被清空之后会自动执行
②析构函数的作用
比如说server端接受很多客户端的连接,当你手动屏蔽你的sever端的时候,这个时候客户端还在正常的连接,如果sever端用类写的,你就可以delete server端的同时,在__del__()写一些东西,说去close掉很多客户端的连接。说白了,析构函数就是做一些程序的收尾工作。
六、类的继承
1、概述
类的公有属性和类的私有属性,其实就是类的封装,下面我们来讲讲继承,是面向对象的第二大特性。
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”,继承的过程,就是从一般到特殊的过程。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
- 实现继承是指使用基类的属性和方法而无需额外编码的能力。
- 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法,OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段
2、继承的定义
在类名的括号中写入需要继承的类名即可
class Person(object):
def talk(self):
print("Talking....")
class BlackPerson(Person): #继承Person这个类
def walk(self): #定义子类自身的walk方法
print("BlackPerson is Walking....")
a = BlackPerson()
a.talk() #由于继承了父类的talk()方法,所以可以被调用
a.walk() #调用子类自身的walk方法
#结果
Talking....
BlackPerson is Walking....
3、构造方法的继承
说明:因为子类有自己的属性,但是又想继承父类的属性,所以需要先继承,再重构
继承类的构造方法2种写法:
- 经典类写法:父类.__init(self,name,age)
- 新式类写法:super(子类,self).__init__(name,age)
注:建议使用新式类的写法,因为使用经典类的写法,在多继承的情况下,会出现重复调用参数的可能
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
self.sex = "girl"
def talk(self):
print("Talking....")
class BlackPerson(Person): #继承Person这个类
def __init__(self,name,age,strength):#定义时需要传入父类的属性名
Person.__init__(self,name,age) #继承父类的构造方法,也可以写成:super(BlackPerson,self).__init__(name,age)
self.strength = strength #定义子类本身的属性
print(self.name,self.age,self.sex,self.strength)
def walk(self): #定义子类自身的walk方法
print("BlackPerson is Walking....")
b = BlackPerson("peng",18,"strong")
#结果
peng 18 girl strong
为什么要继承父类中的方法?可以重写一遍的啊。重新写一遍的话,只能继承self.name和self.age,那self.sex怎么办,它也需要重写吗?所以啊,只有把父类中的构造函数全部继承过来,只能用上面这种办法?那它是怎么实现的呢?我们来画一个图:
4、子类对父类方法的重写
我们可以重写父类中的方法,当然还可以继承父类中的方法
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
self.sex = "girl"
def talk(self):
print("Talking....")
class BlackPerson(Person): #继承Person这个类
def talk(self): #重写父类的方法
Person.talk(self) # 调用父类的方法
print("BlackPerson is Talking....")
def walk(self): #定义子类自身的walk方法
print("BlackPerson is Walking....")
b = BlackPerson("peng",18) #子类不写,则继承父类的构造方法
b.talk()
b.walk()
#结果
Talking....
BlackPerson is Talking....
BlackPerson is Walking....
重写并不是子类的方法名和父类的中的方法名一样就算重写了
重写的条件:
- 重写方法的方法名必须和父类中被重写的方法名一模一样
- 重写方法的传入的参数名和参数的个数必须和父类中被重写的方法一样
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self, food):
print("person is talking...{0}".format(food))
class BlackPerson(Person):
def talk(self): # 方法名和父类的方法名一样,但是少了一个food参数
print("BlackPerson is talking ...")
b = BlackPerson("xiaogao", 18)
b.talk()
#结果
BlackPerson is talking ...
上面的例子不是重写,根据重写的条件明显两个方法的传入参数名和参数的个数都不一样,其实上面这种只是子类自己写了一个talk方法,只是名字一样,但是传入的参数和参数的个数不一样,并不是重写了父类中的方法。下面这个才是真正的重写:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self, food):
print("person is talking...{0}".format(food))
class BlackPerson(Person):
def talk(self,food): #重写父类的方法(方法名和传入的参数名以及参数的个数与父类的方法一样)
print("BlackPerson is talking ...{0}".format(food))
b = BlackPerson("xiaogao", 18)
b.talk("hotdog")
#结果
BlackPerson is talking ...hotdog
5、练习
七、新式类VS经典类
在python还支持多继承,但是一般我们很少用,有些语言干脆就不支持多继承,有多继承,就会带来两个概念,经典类和新式类
1、多继承
子类可以继承多个父类,就叫多继承
class SchoolMember(object): #SchoolMember类
def tell(self):
print("the schoolmember is tell...")
class School(object): #School类
def open_branch(self,addr):
print("open a new branch in",addr)
class Teacher(SchoolMember,School): #子类Teacher同时继承了SchoolMember,School两个类
def teaching(self):
print("The teacher is teaching Math")
t1 = Teacher()
t1.tell() #拥有父类SchoolMember的tell方法
t1.open_branch("shnaghai") #拥有父类School的open_branch方法
#结果
the schoolmember is tell...
open a new branch in shnaghai
2、 新式类
(1) 概念新式类定义时必须继承object类,被定义继承了object类的,就叫做新式类
class Person(object): #继承object类
"新式类"
(2)继承构造方法
新式类初始化构造方法用super关键字去继承
super(子类,self).__init__(name,age)
(3)调用父类中相同方法或者相同属性的顺序
新式类多继承的调用方法是顺序是:广度优先查询
a、先找自己的属性,输出D
class A(object):
def __init__(self):
self.n="A"
class B(A):
def __init__(self):
self.n="B"
class C(A):
def __init__(self):
self.n="C"
class D(B,C):
def __init__(self):
self.n="D"
d = D()
print(d.n)
#结果
D
b、注释D类中代码
class A(object):
def __init__(self):
self.n="A"
class B(A):
def __init__(self):
self.n="B"
class C(A):
def __init__(self):
self.n="C"
class D(B,C):
pass
d = D()
print(d.n)
#结果
B
c、注释B类中的代码
class A(object):
def __init__(self):
self.n="A"
class B(A):
pass
class C(A):
def __init__(self):
self.n="C"
class D(B,C):
pass
d = D()
print(d.n)
#结果
C
d、注释C类中的代码
class A(object):
def __init__(self):
self.n="A"
class B(A):
pass
class C(A):
pass
class D(B,C):
pass
d = D()
print(d.n)
#结果
A
3、经典类
(1)经典类定义,什么都不继承
class Person:
"经典类"
(2)继承构造方法
父类.__init(self,name,age)
(3)调用父类中相同方法或者相同属性的顺序
经典类多继承的调用方法是顺序是:深度优先查询
a、全部
class A: #经典类
def __init__(self):
self.n="A"
class B(A):
pass
def __init__(self):
self.n="B"
class C(A):
def __init__(self):
self.n="C"
class D(B,C):
def __init__(self):
self.n="D"
d = D()
print(d.n)
#结果
D
b、注释D类中代码
class A: #经典类
def __init__(self):
self.n="A"
class B(A):
pass
def __init__(self):
self.n="B"
class C(A):
def __init__(self):
self.n="C"
class D(B,C):
pass
d = D()
print(d.n)
#结果
B
c、注释B类中的代码
class A: #经典类
def __init__(self):
self.n="A"
class B(A):
pass
class C(A):
def __init__(self):
self.n="C"
class D(B,C):
pass
d = D()
print(d.n)
#结果
C
d、注释A类中的代码
class A: #经典类
pass
class B(A):
pass
class C(A):
def __init__(self):
self.n="C"
class D(B,C):
pass
d = D()
print(d.n)
#结果
C
4、总结
- 新式类继承object类,经典类不继承任何类
- 新式类用super关键字继承构造方法,经典类用 父类.__init(self)来继承
- 新式类:广度优先查询,经典类:深度优先查询(因为新式类讲究的是新,所以要找最近的,最新的;然后经典的讲究古老,所以更远更深的)
- 值得注意的是,我们上面是在python2中做的,在python3中不管是经典类还是新式类,都是采用的是广度优先查询,已经废弃2中的深度查询了