基础概念
(1)类: 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例,世间万物,皆可分类。
(2)一切皆为对象
(3)对象:类的实体。eg..\一个叫Amy的可爱小女孩
(4)方法:人会走,会思考\狗会叫,会咬人\即定义一个类的各个功能(类中定义的函数)
(5)继承:即一个派生类继承基类。继承也允许把一个派生类的对象作为一个基类对象对待。
(6)方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖,也称为方法的重写。
(7)实例化:创建一个类的实例,类的具体对象。
创建类
eg:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self, msg=0):
self.msg = msg
if self.msg != 0:
print("My name is %s, i'm %s years old." % (self.name, self.age))
else:
print("hahaha")
p = Person("zhuzhuzhu", "18")
p.talk("Hello, My name is %s " % p.name)
init()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法
类的方法与普通的函数有一个特别的区别——它必须传入一个额外的参数名称, 一般大家习惯传入self,self代表类的实例,虽然在调用时不需要传入对应的参数,但是在定义类时是必须要有的
创建实例对象以及访问属性
类的实例化过程类似于函数的调用,我们可以通过类的名称来进行类的实例化~
eg:
对于上例中创建的类,我们来进行类的实例化
# 创建Person类的第一个实例对象
p1 = Person("zhuzhuzhu", "18")
# 创建Person类的第二个实例对象
p2 = Person("yanyanyan", "16")
之后,我们可以通过点号·来访问对象属性,也可对类的属性进行修改,删除,添加等操作
p1.talk()
p2.talk()
p1.salary = 10000 # 增加了“salary”这个属性
p1.salary =20000 # 对该属性进行修改
del p1.salary # 删除salary属性
另外,我们还可以使用以下函数的形式来访问属性
getattr(obj, name[, default]) : 访问对象的属性。
hasattr(obj,name) :检查是否存在一个属性。
setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
delattr(obj, name) : 删除属性。
hasattr(p1, 'salary') # 如果存在 'salary' 属性返回 True。
getattr(p1, 'salary') # 返回 'salary' 属性的值
setattr(p1, 'salary', 10000) # 添加属性 'salary' 值为 10000
delattr(p1, 'salary') # 删除属性 'salary'
类的继承
eg:
class Animal: # 定义父类
def __init__(self): # 调用父类构造函数
print("我是动物")
def talk(self): # 调用父类方法
print("动物叫")
class Dog(Animal): # 定义子类
def __init__(self): # 调用子类构造函数
print("我是小狗")
def bark(self): # 调用父类方法
print("汪汪汪")
d = Dog() #实例化子类
d.talk() #调用父类方法
d.bark() #调用子类方法
OUTPUT
我是小狗
动物叫
汪汪汪
也可以继承多个类
class A: # 定义类 A
.....
class B: # 定义类 B
.....
class C(A, B): # 继承类 A 和 B
.....
方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法,即当子类的方法覆盖了父类的同名方法,调用时会执行子类方法:
eg:
上例中,父类有一个方法“talk”,调用该方法会输出“动物叫”,而我的需求是让它输出“小狗叫”,则可以在子类中进行方法的重写。
class Animal: # 定义父类
def __init__(self): # 调用父类构造函数
print("我是动物")
def talk(self): # 调用父类方法
print("动物叫")
class Dog(Animal): # 定义子类
def __init__(self): # 调用子类构造函数
print("我是小狗")
def talk(self):
print("小狗叫")
def bark(self): # 调用父类方法
print("汪汪汪")
d = Dog() #实例化子类
d.talk() #调用重写方法
d.bark() #调用子类方法
OUTPUT
我是小狗
小狗叫
汪汪汪
多态
要理解什么是多态,我们首先要对数据类型再作一点说明。当我们定义一个class的时候,实际上就定义了一种数据类型。
a = list() # a是list类型
b = Dog() # b是Dog类型
用isinstance来判断某一个变量是否属于某个数据类型:
即:
isinstance(a, list)
isinstance(b, Dog)
OUTPUT1 : true
OUTPUT2 : true
但是我们发现,当输入isinstance(b, Animal)时,返回结果也为true,这就说明,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。
反过来则不成立。
接着,我们定义一个让动物叫两次的函数,调用该函数时传入Animal类型的实参,即使它接收一个Animal类型的变量:
def talk_twice(animal):
animal.talk()
animal.talk()
talk_twice(Animal())
OUTPUT :
动物叫
动物叫
talk_twice(Dog())
OUTPUT :
小狗叫
小狗叫
此时,若我们再定义一个从Animal类派生的类Duck:
class Duck(Animal):
def talk(self):
print("鸭子叫")
OUTPUT:
鸭子叫
鸭子叫
你会发现,新增一个Animal的子类,不必对talk_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
因此,一个变量,只要确定它是Animal类或者是Animal的子类,我们就可以放心的调用Animal里的方法。
这便是多态的优势:调用方只管调用,不管细节。
开闭原则:
对扩展开放:允许新增Animal子类;
对修改封闭:不需要修改依赖Animal类型的talk_twice()等函数。
总结:
继承可以把父类中的所有方法拿来供子类使用,子类只需要添加新增的方法并且修改覆盖父类中不适合的方法即可。
有了继承,才能有多态!
类的属性与方法
类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用 self.__private_methods
eg:
class Person:
__private = "talk"
public = "eat"
def __init__(self, run, smile, cry):
self.run = run
self.smile = smile
self.cry = cry
def behavior(self):
print("I'm a good man,because I love %s." % self.smile)
zhuzhuzhu = Person("lalala", "hahaha", "wuwuwu")
zhuzhuzhu.behavior()
print(zhuzhuzhu.public)
print(zhuzhuzhu.__private)
此时会报一个错:
File “E:/python study/Code/PrivateClass.py”, line 23, in
print(zhuzhuzhu.__private)
AttributeError: ‘Person’ object has no attribute ‘__private’
Process finished with exit code 1
这是因为,实例不能访问私有变量。Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName (对象.私有类名.私有属性名)访问属性,即用以下代码来替换上例中的print(zhuzhuzhu.__private)
print(zhuzhuzhu._Person__private)
几个区别
单下划线、双下划线、头尾双下划线说明:
1__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 init() 之类的。
2_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
3__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。