概述
面向对象编程(Object Oriented Programming,OOP),是一种程序设计思想,把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向过程的程序设计把计算机程序视为一系列命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。而面向对象的程序设计把计算机程序视为一组对象集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
那面向对象编程与面向过程编程到底有什么区别?通过一个例子进行比较:
#面向过程
stu1 = {"name":"aaa","score":90}
stu2 = {"name":"bbb","score":85}
def print_score(stu):
print("%s: %s" %(stu["name"],stu["score"]))
print_score(stu1) #aaa: 90
print_score(stu2) #bbb: 85
------------这是分界线-----------------
#面向对象,面向对象的设计思想是抽象出class,根据class创建instance
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))
stu1 = Student("aaa",90) #创建实例stu1
stu2 = Student("bbb",85) #创建实例stu2
stu1.print_score() #调用方法,输出aaa: 90
stu2.print_score() #调用方法,输出bbb: 85
1.类和实例
面向对象最重要的概念就是类(class)和实例(instance),类是抽象的模板,如上述Student类,而实例是根据类创建出来的一个个具体“对象”,如上述stu1对象。
#Python类定义格式
class Classname(object):
psss
#说明:class关键字定义类,后面紧接类名(类名通常首字母大写),紧接着为(object),表该类是从哪个类继承下来的。如果没有合适的继承类,就用object类,这是所有类最终都会继承的类。
#Python实例
instancename = Classname() #创建了实例instancename,创建实例通过类名+()实现
__init__
class Student(object):
#实例属性
#__init__()叫做初始化方法(构造方法),在类被调用时,这个方法会自动执行,进行一些初始化动作。
def __init__(self,name,score):
self.name = name
self.score = score
#实例方法
def print_score(self):
print("%s: %s" %(self.name,self.score))
#析构方法,在实例释放、销毁的时候自动执行,通常用于做一些收尾工作。这个方法可选,不是必须写
def __del__(self):
print("这里是析构函数,结束啦!")
stu1 = Student("张三",90)#生成一个角色,会自动把参数传给Student下面的__init__()方法,此时相当于Student(stu1,"张三",90)
stu1.print_score()#此时相当于Student.print_score(stu1)
注意到__init__
方法的第一个参数永远是self
,表示创建的实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身。有了__init__
方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__
方法匹配的参数,但self
不需要传,Python解释器自己会把实例变量传进去:
stu1 = Student("aaa",90) #实例stu1
print(stu1.name) #aaa
print(stu1.score) #90
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别!
2.访问限制
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))
s1 = Student('zhangsan',90)
print(s1.name)#打印实例名称,这里输出为zhangsan
print(s1.score)#打印实例成绩,这里输出为90
s1.score = 100 #在类外部,我们修改实例属性
print(s1.score)#打印输出为修改后的100
通过上述代码可以发现,类虽然隐藏了内部的复杂逻辑,但是其内部属性可以被外部代码任意修改其属性,有没有什么方法可以不让外部代码修改呢?在Python中,实例变量名如果以 __ 开头,就变成了一个私有变量,只有内部可以访问,外部不能访问,所以,上述代码可以这么修改:
class Student(object):
def __init__(self,name,score):
self.__name = name #原self.name修改为self.__name
self.__score = score #原self.score修改为self.__score
def print_score(self):
print('%s: %s' %(self.__name,self.__score)) #修改
s1 = Student('zhangsan',90)
print(s1.__name)#报错,AttributeError: 'Student' object has no attribute '__name'
print(s1.__score)#报错,AttributeError: 'Student' object has no attribute '__score'
#说明:这里已经无法从外部访问 实例变量.__name和实例变量.__score
上述修改确保了外部代码不能随意修改对象内部状态,但是改的太过于彻底了,我们都无法访问其属性了~~~~。如果我们还想获取属性name与score应该怎么办?可以给Student类增加get_name和get_score方法。
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))
def get_name(self):
return self.__name #获取name属性
def get_score(self):
return self.__score #获取score属性
def set_name(self,name):
self.__name = name #修改name属性
return self.__name
def set_score(self,score):
self.__score = score #修改score属性
s1 = Student('zhangsan',90)
print(s1.get_name())#zhangsan
print(s1.get_score())#90
print(s1.set_name('lisi')) #lisi
说了半天,这样一番周折(看起来有点化简为繁,代码量增加)有什么优势?其实这样相比于直接s1.score = 100,在set_score方法中,可以对参数做检查,更符合实际要求。
class Student(object):
...
def set_score(self,score):
if 0<=score<=100:
self.__score = score
else:
raise ValueError('成绩不规范')
在Python中,变量名类似 __xxx__ ,即双下划线开头,并且双下划线结尾的,是特殊变量,可以直接访问;
变量名类似_xxx,即一个下划线开头,这种实例变量外部是可以访问的,按照约定成俗的规定,这样的变量意思是“虽然可以被访问,但是,请将其视为私有变量,不要随意访问”
变量名类似__xxx,即双下划线开头,这种变量不可以被外部访问,需要在类内部写相应方法。是不是外部绝对不能访问呢?实际上也不是,实际上通过 _类名__xxx仍可以访问到 __xxx变量,但是强烈不建议这么干!!!
3.封装、继承和多态
(1)继承
#继承
class Person(object):
def speak(self):
print('People is speaking...')
class Teacher(Person):
pass
class Student(Person):
pass
teacher1 = Teacher()
teacher1.speak() #People is speaking...
student1 = Student()
student1.speak() #People is speaking...
#注:Person为父类,Teacher、Student为其子类,子类自动继承父类方法
#继承--新市类super用法
class Persion(object):
def __init__(self,name,age):
self.name = name
self.age = age
def speak(self):
print("People is speaking...")
class Teacher(Persion):
def __init__(self,name,age,course,salary):
#Persion.__init__(self,name,age) #父类实现,另一种方法见super,这种写法多继承不太方便
super(Teacher, self).__init__(name,age)
self.course = course
self.salary = salary
def teaching(self):
print("Teacher %s is teaching %s"%(self.name,self.course))
class Student(Persion):
def __init__(self,name,age,grade,sid):
#Persion.__init__(self,name,age) #父类实现,另一种方法见super,这种写法多继承不太方便
super(Student, self).__init__(name,age)
self.grade = grade
self.sid = sid
def study(self):
print("The student %s ID is %s"%(self.name,self.sid))
teacher1 = Teacher("Jack",30,"C++",3000)
teacher1.teaching() #Teacher Jack is teaching C++
student1 = Student("Tom",20,"大一","201911")
student1.study() #The student Tom ID is 201911
(2)多态
#多态
class Person(object):
def speak(self):
print('People is speaking...')
class Teacher(Person):
def speak(self):
print('Teacher is speaking...')
def work(self):
print('Teacher is working...')
class Student(Person):
def speak(self):
print('Student is speaking...')
def learn(self):
print('Student is learning...')
teacher1 = Teacher()
teacher1.speak() #Teacher is speaking...
student1 = Student()
student1.speak() #Student is speaking...
#注:当子类和父类都存在相同的speak()方法时,子类的speak()覆盖了父类的speak(),在代码运行的时候总是调用子类的speak()
#开闭原则
class Person(object):
def speak(self):
print('People is speaking...')
class Teacher(Person):
def speak(self):
print('Teacher is speaking...')
def work(self):
print('Teacher is working...')
class Student(Person):
def speak(self):
print('Student is speaking...')
def learn(self):
print('Student is learning...')
#新增一个Person子类,不必对run_twice()做任何修改,任何依赖Person作为参数的函数或者方法都可以不加修改正常运行,原因就在于多态
class Test(Person):
def speak(self):
print('Testing...')
def run_twice(people):
people.speak()
people.speak()
run_twice(Person()) #People is speaking... People is speaking...
run_twice(Teacher()) #Teacher is speaking... Teacher is speaking...
run_twice(Student()) #Student is speaking... Student is speaking...
run_twice(Test()) #Testing... Testing...
多态的好处就是,当我们需要传入 Teacher、Student、Test...时,我们只需要接收Person类型就可以了,因为 Teacher、Student、Test...都是Person类型,然后按照Person类型进行操作即可。由于Person类型有speak()方法,因此,传入任意类型,只要是Person类或者子类就会自动调用实际类型的speak()方法,这就是多态的意思:
对于一个变量,我们只需要知道它是Person类型,无需确切地知道它的子类型,就可以放心地调用speak()方法,而具体调用的speak()方法是作用在Person、Teacher、Student、还是Test对象上,由运行该对象的确切类型决定,这就是多态的真正威力:调用方只管调用,不管细节,而我们新增一种Person的子类时,只需确保speak()方法编写正确,不用管原来代码是如何调用的,这就是著名的“开闭”原则:
对扩展开放:允许新增Person子类
对修改封闭:不需要修改依赖Person类型的run_twice()等函数
静态语言VS动态语言
静态语言(如Java):如果需要传入Person类型,则传入的对象必须是Person类型或者它的子类,否则,将无法调用speak()方法。
动态语言(如Python):不一定需要传入Person类型,我们只需要保证传入的对象有一个speak()方法就可以了。
#鸭子类型:并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做鸭子
class Person(object):
def speak(self):
print('People is speaking...')
class Teacher(Person):
def speak(self):
print('Teacher is speaking...')
def work(self):
print('Teacher is working...')
class Student(Person):
def speak(self):
print('Student is speaking...')
def learn(self):
print('Student is learning...')
#新增一个Person子类,不必对run_twice()做任何修改,任何依赖Person作为参数的函数或者方法都可以不加修改正常运行,原因就在于多态
class Test(Person):
def speak(self):
print('Testing...')
#鸭子类型,不需要判断参数是Person类型,只要对象有speak()方法,就能调用成功!
class Timer(object):
def speak(self):
print("鸭子类型...")
def run_twice(people):
people.speak()
people.speak()
run_twice(Person()) #People is speaking... People is speaking...
run_twice(Teacher()) #Teacher is speaking... Teacher is speaking...
run_twice(Student()) #Student is speaking... Student is speaking...
run_twice(Test()) #Testing... Testing...
run_twice(Timer()) #鸭子类型... 鸭子类型...
总结:a、继承可以把父类的所有功能直接拿过来,这样不必重零做起,子类只需新增自己特有方法,也可以把父类不适合的方法覆盖重写。
b、动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。
4.type()和isinstance()
type()主要用于获取未知变量的类型
isinstance()只要用于判断子类是否继承父类
isinstance()可以判断子类对象是否继承与父类,而type()不可以
dir()
获取一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
#dir()函数
>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>>
#getattr、setattr、hasattr
>>> class MyObject(object):
... def __init__(self):
... self.x = 9
... def power(self):
... return self.x * self.x
...
>>> obj = MyObject()
>>> hasattr(obj,'x') #有属性"x"吗?
True
>>> hasattr(obj,'y') #有属性"y"吗?
False
>>> setattr(obj,'y',2) #设置一个属性"y"
>>> hasattr(obj,'y')
True
>>> getattr(obj,'y') #获取属性"y"
2
>>> getattr(obj,'x') #获取属性"x"
9
>>> getattr(obj,'z') #获取属性"z",属性不存在抛出AttributeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyObject' object has no attribute 'z'
5.实例属性和类属性
#实例属性和类属性
class Person(object):
city = 'XIAN' #类属性,可以直接在类中定义,归Person类所有
def __init__(self,name):
self.name = name
person1 = Person('Tom') #创建实例
person1.age = 24 #给实例绑定age属性
person1.city
print(person1.name) #Tom
print(person1.age) #24
print(person1.city) #XIAN
person1.city = 'Shanghai' #给实例绑定city属性,由于实例属性优先级比类属性高,因此会屏蔽掉类的city属性
print(person1.city) #Shanghai
del person1.city #删除实例city属性
print(person1.city) #XIAN,再次调用person1.city,由于实例city属性没有找到,类的city属性显示
#注:在编写程序时,千万不要对实例属性和类属性使用相同的名字,因为相同的名称实例属性将屏蔽类属性,但是当删除实例属性后,再使用相同的名称,访问到的是类属性
#练习:统计学生人数——每创建一个实例,该属性自动增加
class Student(object):
count = 0 #实例属性,属于所有类所有,所有实例共享一个属性
def __init__(self,name):
self.name = name
Student.count += 1
s1 = Student('AAA')
s2 = Student('BBB')
s3 = Student('CCC')
print(Student.count) #3
6.经典类vs新式类
#多继承新式类、经典类区别
#经典类:深度优先 新式类:广度优先
class A:
def __init__(self):
self.n = 'A'
class B(A):
# def __init__(self):
# self.n = 'B'
pass
class C(A):
# def __init__(self):
# self.n = 'C'
pass
class D(B, C):
# def __init__(self):
# self.n = 'D'
pass
obj = D()
print(obj.n)
7.静态方法、类方法、属性方法
(1)静态方法
通过@staticmethod装饰器即可以把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self. 调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法。
#静态方法
# -*- coding:utf-8 -*-
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod #把eat方法变为静态方法
def eat(self):
print("%s is eating"%self.name)
d = Dog("大黄")
#d.eat() #这样调用会报错,当eat变为静态方法以后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了
#方法一:调用时主动传递实例本身给eat方法,即d.eat(d)
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod #把eat方法变为静态方法
def eat(self):
print("%s is eating"%self.name)
d = Dog("大黄")
d.eat(d)
#方法二:在eat方法中去掉self参数,但这也意味着在eat中不能通过self.调用实例中的其他变量了
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod #把eat方法变为静态方法
def eat():
print("%s is eating"%("二黄"))
d = Dog("大黄")
d.eat()
(2)类方法
类方法通过@classmethod装饰器实现,类方法和普通方法的区别是,类方法只能访问类变量,不能访问实例变量。
#类方法
class Dog(object):
def __init__(self,name):
self.name = name
@classmethod
def eat(self):
print("%s is eating"%self.name)
d = Dog("大黄")
d.eat() #报错,因为name是个实例变量,类方法不能访问实例变量
#可以定义一个类变量,叫name1
class Dog(object):
name1 = "这里是类变量"
def __init__(self,name):
self.name = name
@classmethod
def eat(self):
print("%s is eating"%self.name1)
d = Dog("大黄")
d.eat() #这里是类变量 is eating
(3)属性方法
属性方法的作用就是通过@property把一个方法变为一个静态属性
#属性方法
class Dog(object):
def __init__(self,name):
self.name = name
@property
def eat(self):
print("%s is eating"%self.name)
d = Dog("大黄")
d.eat() #报错,因为eat此时已经变成一个静态属性了,不是方法了,想调用已经不需要加()了,直接d.eat就可以了
#直接d.eat
class Dog(object):
def __init__(self,name):
self.name = name
@property
def eat(self):
print("%s is eating"%self.name)
d = Dog("大黄")
d.eat
把一个方法变成静态属性有什么用呢?既然想要静态变量,那直接定义成一个静态变量不就得啦?以后你会遇到很多场景是不能 简单通过定义静态属性来实现的,比如,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了,想知道这种状态必须经历以下几步:
①连接航空公司API查询
②对查询结果进行解析
③返回结果给你的用户
因此这个status属性的值是一系列动作后才得到的结果,所以每次调用时,其实它都要经过一系列的动作才返回结果,但这些动作过程不需要用户关心,用户只需要调用这个属性就可以。
class Flight(object):
def __init__(self,name):
self.flight_name = name
def checking_status(self):
print("checking flight %s status " % self.flight_name)
return 1
@property
def flight_status(self):
status = self.checking_status()
if status == 0 :
print("flight got canceled...")
elif status == 1 :
print("flight is arrived...")
elif status == 2:
print("flight has departured already...")
else:
print("cannot confirm the flight status...,please check later")
f = Flight("东方航空")
f.flight_status
现在我只能查询航班状态, 既然这个flight_status已经是个属性了, 那我能否给它赋值呢?试试吧
f = Flight("东方航空")
f.flight_status
f.flight_status = 2
输出, 说不能更改这个属性,怎么办怎么办???当然可以改, 不过需要通过@proerty.setter装饰器再装饰一下,此时你需要写一个新方法, 对这个flight_status进行更改。当然@flight_status.deleter允许可以将这个属性删除 。
# -*- coding:utf-8 -*-
class Flight(object):
def __init__(self,name):
self.flight_name = name
def checking_status(self):
print("checking flight %s status"%self.flight_name)
return 1
@property
def flight_status(self):
status = self.checking_status()
if status == 0:
print("flight got canceled...")
elif status == 1:
print("flight is arrived...")
elif status == 2:
print("flight has departured already...")
else:
print("cannot confirm the flight status...,please check later")
@flight_status.setter
def flight_status(self,status):
status_dic = {
0:"canceled",
1:"arrived",
2:"departured"
}
print("has changed the flight status to ",status_dic.get(status))
@flight_status.deleter
def flight_status(self):
print("status got removed...")
f = Flight("东方航空")
f.flight_status
f.flight_status = 2
del f.flight_status
最后
以上就是生动魔镜为你收集整理的Python面向对象的全部内容,希望文章能够帮你解决Python面向对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复