笔记内容由廖雪峰官网和菜鸟教程整理得到
文章目录
面向对象编程的设计思想:抽象出Class类,根据Class创建Instance实例。
Class既包含数据又包含数据操作,面向对象的抽象程度比函数高。
数据封装、继承、多态是面向对象的三大特点。
类和实例
Class类是创建Instance实例的模板,而Instance实例是一个个具体对象。
每个实例拥有的数据都相互独立,Python允许对实例变量绑定任何的数据,同一个类的不同实例可以拥有不同的变量。
#-*- coding:utf-8 -*-
#定义类用class关键字,后面Student是类名
#括号内表示继承的类,没有合适继承用object表示所有类都会继承的类
class Student(object):
#定义__init__,创建实例时强制填写必须属性
#__init__第一个参数永远是self,指向创建实例本身
def __init__(self, name, score):
self.name = name
self.score = score
#在类内定义访问数据的函数以封装数据
#数据和逻辑被封装起来,不必知道内部实现即可调用
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
#必须传入和__init__匹配的参数,self不需要传,会自动传入
#不能传入其他的参数
lisa = Student('Lisa', 99)
bart = Student('Bart', 59)
#调用数据和函数时要加实例名
print(lisa.name, lisa.score,lisa.get_grade())
print(bart.name, bart.score,bart.get_grade())
#允许同一个类中的实例拥有不同的变量
bart.age=8
print(bart.age)
lisa.gender='female'
print(lisa.gender)
#output
#Lisa 99 A
#Bart 59 C
#8
#female
访问限制
- private
在__init__定义的属性名称前加上两个下划线,表示private,只能内部访问的变量,保证外部代码不能随意修改对象内部状态。 - __xxx __
在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量。python中如果定义变量名前有一个下划线,则默认将其视为私有变量,不要随意访问。 - 外部访问
外部需要通过函数来访问或者修改private变量。
不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量。【不同版本Python不同,但尽量不要这样访问】
#-*- coding:utf-8 -*-
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
#增加返回变量的函数,以访问private变量
def get_name(self):
return self.__name
def get_score(self):
return self.__score
#增加用来修改private变量的函数
def set_score(self, score):
self.__score = score
#利用函数来访问private变量
bart=Student('Bart',59)
print(bart.get_name())
print(bart.get_score())
#利用函数修改private变量
bart.set_score(100)
print(bart.get_score())
#内部的__name变量已经被Python解释器自动改成了_Student__name
# 外部代码只是给bart新增了__name变量
bart.__name='New'
print(bart.__name)
print(bart.get_name())
#output
#Bart
#59
#100
#New
#Bart
- 作业练习:请把下面的Student对象的gender字段对外隐藏起来,用get_gender()和set_gender()代替,并检查参数有效性:
class Student(object):
def __init__(self, name, gender):
self.name = name
self.__gender = gender
def get_gender(self):
return self.__gender
def set_gender(self,gender):
self.__gender=gender
# 测试:
bart = Student('Bart', 'male')
if bart.get_gender() != 'male':
print('测试失败!')
else:
bart.set_gender('female')
if bart.get_gender() != 'female':
print('测试失败!')
else:
print('测试成功!')
继承与多态
继承
- 语法
class 子类名(父类名):
不继承父类的类声明用object class 类名:父类 - 特点
1、子类中构造函数存在三种情况:不重写父类、重写父类、重写同时继承父类
2、类中调用普通函数不需要加上self参数,但调用父类的函数时需要加上父类的类名前缀同时加上self参数的变量。
3、现在本类中查找函数,找不到再到基类中查找。 - 判断子类
issubclass(Classson,Classfather) - 布尔函数判断一个类是另一个类的子类或者子孙类。
isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
__init__子类继承父类构造函数的三种情况
1、不重写__init__则调用父类定义的__init__
2、重写__init__则调用子类定义的__init__
3、super重写__init__的同时继承母类定义的__init__
super(子类名称,self).__init__(父类的参数)
父类名称.__init__(self,父类参数)
#-*- coding:utf-8 -*-
class Animal(object):
def __init__(self,number):
self.number=number
print('number in Animal is %d' %(self.number))
def getnumber(self):
print('Animal',self.number)
animal=Animal(1)
animal.getnumber()
#number in Animal is 1
#Animal 1
#不重写__init__则调用父类定义的__init__
class Cat(Animal):
def getnumber(self):
print('Cat',self.number)
#return self.number
#不重写__init__则调用父类定义的__init__
cat=Cat(50)
#子类中重写函数则调用重写后的函数
cat.getnumber()
#output
#number in Animal is 50
#Cat 50
#重写__init__则调用子类定义的__init__
class Dog(Animal):
def __init__(self,number):
self.number=number
print('number in Dog is %d' %(self.number))
def getnumber(self):
print('Dog',self.number)
# return self.number
dog=Dog(100)
dog.getnumber()
#output
#number in Dog is 100
#Dog 100
#重写__init__的同时继承父类定义的__init__
class Pig(Animal):
def __init__(self,number,name):
#super(子类名称,self).__init__(父类的参数)
#父类名称.__init__(self,父类参数)
#等价于Animal.__init__(self,number)
super(Pig,self).__init__(number)
self.name=name
print('name in Pig is %s' %(self.name))
print('number in Pig is %d' %(self.number))
def getnumber(self):
print('Pig',self.number)
# return self.number
pig=Pig(101,'ann')
pig.getnumber()
#output
#number in Animal is 101
#name in Pig is ann
#number in Pig is 101
#Pig 101
#判断Pig类是否是Animal子类
print(issubclass(Pig,Animal))
#判断实例对象pig是否是Animal的类或子类的实例对象
print(isinstance(pig,Animal))
- 多态
Python动态语言‘file-like object,想要调用某种方法不一定要继承,只要保证传入参数是个对象,且具有该相似方法即可。而静动态语言如JAVA等则必须继承。
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
def eat(self):
print('Eating meat...')
class Cat(Animal):
pass
#由于Animal类型有run()方法
#只要传入Animal类或者子类,就会自动调用实际类型的run()方法
#当新增Animal子类时,不必对run_twice修改即可使用
def run_twice(animal):
animal.run()
animal.run()
#子类和父类使用run_twice
animal=Animal()
dog=Dog()
cat=Cat()
run_twice(animal)
run_twice(dog)
run_twice(cat)
#output
#Animal is running...
#Animal is running...
#Dog is running...
#Dog is running...
#Animal is running...
#Animal is running...
#file-like object
#只要有run()函数的对象,就可以调用run_twice
class Duck(object):
def run(self):
print('file-like object')
duck=Duck()
run_twice(duck)
#output
#file-like object
#file-like object
获取对象信息
判断类型(重点isinstance)
相同返回True,否则返回False
- isinstance和issubclass
类与类:issubclass(Classson,Classfather) - 布尔函数判断一个类是另一个类的子类或者子孙类。
实例与类:isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
isinstance(arg,(int,str,tuple)) - isinstance和type
type:
type不考虑继承,不认为子类是父类的类型
class type(object)
| type(object_or_name, bases, dict)
| type(object) -> the object’s type 输出obj的类型
| type(name, bases, dict) -> a new type
print(type(123))
#<class 'int'>
print(type(123)==int)
#True
isinstance
isinstance考虑继承,认为子类实例既是子类类型,也是父类的类型
isinstance(obj, class_or_tuple, /)
isinstance(x, (A, B, …))判定obj是否是列表内中某一种(type不行)
>>> isinstance(d, Dog) and isinstance(d, Animal)
True
>>> isinstance([1, 2, 3], (list, tuple))
True
对象属性和方法
dir
dir()返回list:对象所有的属性和方法
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
getattr,hasattr,setattr,delattr
hasattr(obj, name)判断是否有obj是否有name的变量,返回True False
getattr(object, name[, default]) -> value获取属性。如果有name属性则返回属性。如果没有name属性,没有defalut则报错,有default则赋值。
setattr(obj, name, value, /)设置属性,obj.name=value。
delattr(obj, name, /)删除属性,删除obj的name属性
(?以下这句话我还是有点不太明白,这种方法除了麻烦还有其它问题吗)
可以拿到class内部的数据,但只有在不知道对象信息的时候,才会去获取对象信息。
可以:
sum=obj.x+obj.y
就不要:
sum=getattr(obj,‘x’)+getattr(obj,‘y’)
class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
obj = MyObject()
#判断是否有属性
#hasattr(obj, name)判断是否有obj是否有name的变量
hasattr(obj,'x')
#True
hasattr(obj,'y')
#False
#获取属性
#getattr(object, name[, default]) -> value
getattr(obj,'x')
#9
getattr(obj,'y')
#找不到报错AttributeError: 'MyObject' object has no attribute 'y'
getattr(obj, 'z', 404)
#找不到则赋值404
# 设置一个属性'y'
setattr(obj, 'y', 19)
hasattr(obj,'y')
#True
getattr(obj,'y')
#19
#setattr也可以更改__init__中的default
setattr(obj, 'x', 19)
obj.x
#361
obj.power()
#361
#delattr删除属性
delattr(obj,'x')
hasattr(obj,'x')
#False
函数获也可以获取对象的方法
class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
obj = MyObject()
hasattr(obj,'power')
%True
#getattr可以用来获取方法,将方法赋值给变量
f=getattr(obj,'power')
f
#<bound method MyObject.power of <__main__.MyObject object at 0x0000000005FE67F0>>
f()#等价于obj.f()
#81
常用重载功能__xxx__
__xxx__的属性和方法都是特殊用途
比如len(‘ABC’)等价于’ABC’.__len__(),当使用len()函数内部自动调用对象的__len__()方法。自己写的类如果需要用len(myobj)的话则在class声明一个__len__( )方法(函数)
__init__(self,args…) 构造函数
__del__(self) 删除一个对象
__repr__(self)转化为供解读器读取的形式?
__str__(self) 转化为适于人阅读的形式,当有print时,返回这个函数中的return
__cmp__(self)对象比较
__add__(self,other):运算符重载
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
#如果没有__str__()
#返回对象<__main__.Vector object at 0x0000000005ADE860>
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
#运算符重载
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print(v1 + v2)
实例属性和类属性
类属性:归Student类所有
实例属性:通过实例变量给实例绑定
编程中不要不要对实例属性和类属性使用相同的名字。因为实例属性的优先级高于类属性,相同名称的实例属性将屏蔽掉类属性。删除实例属性后,如果存在同名的类属性,则访问到的将是类属性。
class Student(object):
school='Sun Yat-sen'#类属性:归Student类所有
def __init__(self,name):
self.name=name#实例属性:通过实例变量给实例绑定属性
class Student_2(Student):
pass
s=Student('Wayne')
print(s.name)#打印实例属性
#Wayne
print(s.school)#打印实例的school属性,因为实例没有则返回类的school属性
#Sun Yat-sen
print(Student.school)#打印类的school属性
#Sun Yat-sen
s.school='PEK'#给实例绑定school属性
print(s.school)#实例属性优先级更高,屏蔽掉类的school属性
#PEK
print(Student.school)#实例属性改变但类的属性保持不变
#Sun Yat-sen
del s.school#删除实例的属性
print(s.school)#无法找到实例的属性,返回类的属性
#Sun Yat-sen
#子类也会继承父类本身绑定的属性
s2=Student_2('Violette')
print(s2.school)#打印实例的school属性
#Sun Yat-sen
print(Student_2.school)#打印类的school属性
#Sun Yat-sen
练习
class Student(object):
count = 0
def __init__(self, name):#类属性不用作为参数
self.name = name
Student.count+=1#调用类属性必须(类名.属性),+=
# 测试:
if Student.count != 0:
print('测试失败!')
else:
bart = Student('Bart')
if Student.count != 1:
print('测试失败!')
else:
lisa = Student('Bart')
if Student.count != 2:
print('测试失败!')
else:
print('Students:', Student.count)
print('测试通过!')