面向对象编程
面向对象编程,在英文中称之为Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。Python是一个纯天然面向对象的编程语言,在Python中所有数据类型都可以视为对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
类与实例化
类:用来描述具有相同的属性和方法的对象的集合,它定义了该集合中每个对象所共有的属性和方法,对象是类的实例。
实例化:通过类创建对象的过程称为实例化。
类是对象的蓝图和模板,而对象是类的实例。类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。
把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装隐藏内部细节,通过继承实现类的特化(specialization)和泛化(generalization),通过多态实现基于对象类型的动态分派。
class Student():def __init__(self,name,age): #构造方法/构造函数),该方法在类实例化时会自动调用self.__name=nameself.age=age'''类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 selfself代表类的实例,而非类'''def talk(self):print('%s is %d years old' %(self.__name,self.age))#私有属性在类内部被重命名以实现私有化,访问时需要通过特定的方法(如getter和setterdef set_name(self,name):self.__name=name
s=Student('liu',10)
s.talk() #liu is 10 years old
s.__name='li' #python3 私有属性或方法是不能被子类直接访问的。如果尝试修改私有属性,
#不会产生语法错误,但修改不会生效
s.age=14
s.talk() #liu is 14 years old
s.set_name('li')
s.talk() #li is 14 years old
继承与多态
定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class),Python 同样支持类的继承。派生类的定义如下class son (base):
子类(派生类/son)会继承父类(基类/base)的属性和方法。
class base:#基本属性name=''__age = 0 #定义私有属性,私有属性在类外部无法直接进行访问def __init__(self,n,a): #构造函数self.name=nself.__age=adef talk(self):print('call base %s is %d years old'%(self.name,self.__age))
class Son(base):pass
s=Son('liu',10) #子类继承了父类的属性和方法
s.talk() #call base liu is 10 years old
class Son_test(base):address=''def __init__(self, n, a,add):super().__init__(n, a)self.address=adddef talk(self): #覆写父类的方法print('son func live in%s'%(self.address))
st=Son_test('zhang',12,'beijing')
st.talk() #son func live inbeijing
b=base('sun',12)
b.talk() #call base sun is 12 years old
def base_talk(base): """接受所有base类的子类实例,并调用它们的talk方法传入的任意类型,只要是base类或者子类,就会自动调用实际类型的talk()方法,这就是多态"""base.talk()
base_talk(st) #son func live inbeijing
base_talk(s) #多态需要实现函数覆盖 call base liu is 10 years old
鸭子类型
python中的鸭子类型是一种动态类型的概念,其核心思想源自于一句格言:“如果它走起路来像鸭子、叫起来也像鸭子,那么它就是鸭子”。这意味着在Python中,一个对象的有效性不是由其类型决定的,而是由其行为决定的。这种类型检查方式强调对象的行为比其具体类型更重要。
class Duck:def quack(self):print('quack')
class Person:def quack(self):print('I am quack like a duck')
def func_quac(duck_lick): #函数可以处理任何 具用quack方法 的对象'''静态类型检查发生在编译期,要求对象必须是某个特定类型的子类,鸭子类型中不需要预定义类型发生在运行时,类型约束由对象的行为决定,而不是继承的类'''duck_lick.quack()
d=Duck()
p=Person()
func_quac(d) #quack
func_quac(p) #I am quack like a duck
多继承
多重继承(多继承)是面向对象编程的一个特性,允许一个子类同时继承多个父类。多重继承在设计中有其用途,但也可能导致复杂性增加,特别是当子类从多个父类中继承相同名称的方法时。多态中方法调用顺序可能受到 __mro__
方法的影响,它展示了方法解析顺序(Method Resolution Order, MRO)。
class A:def __init__(self,n):self._name=ndef hello(self):print('Hello from A')def show_A(self):print('call A func')
class B:def __init__(self,n):self._name=ndef hello(self):print('Hello from B')def show_B(self):print('call B func')
class C(A, B):def __init__(self, n):super().__init__(n)def hello(self):print('Hello from C')def show_C(self):print('call C func')
class D(B, A):pass
# 使用C和D来演示多重继承
c = C('li')
c.hello() # 输出: Hello from C c可掉c.show_A() c.show_B() c.show_C()
d = D('li')
d.hello() # 输出: Hello from B(因为B在继承链中先于A) d.show_B() d.show_A()
print(C.mro()) # 输出: [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
面向对象三大特性
面向对象有三大支柱:封装、继承和多态。封装 "隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口"。在类中定义的方法其实就是把数据和对数据的操作封装起来了,在创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说只需要知道方法的名字和传入的参数,而不需要知道方法内部的实现细节。
继承:可以实现现有类的所有功能,并无需要重新编写原来的类情况下对这些功能进行扩展,
多态:子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写可以让父类的同一个行为在子类中拥有不同的实现版本,当调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态。
属性封装访问
class Student:def __init__(self, n):self.__name = n'''在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头'''def __show(self):print(self.__name)print('__show')
def main():stu = Student('hello')# AttributeError: 'Student' object has no attribute '__show'#stu.__show()# AttributeError: 'Student' object has no attribute '__foo'#print(stu.__name)#Python并没有从语法上严格保证私有属性或方法的私密性,它# 只是给私有的属性和方法换了一个名字来妨碍对它们的访问,下面就是stu._Student__show()print(stu._Student__name)stu.age=10 #实例绑定任何属性和方法,这就是动态语言的灵活性print(stu.age)#stu1=Student('world') #AttributeError: 'Student' object has no attribute 'age'#print(stu1.age) #给一个实例绑定的方法,对另一个实例是不起作用的Student.age=10 #给class绑定属性或方法后,所有实例均可调用:# __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称,在类定义里写上stu1=Student('world')print(stu1.age)if __name__ == "__main__":main()
@property装饰器
Python中属性和方法访问权限的问题中,虽然python不能完全‘私有’ ,但是如果直接将属性暴露给外界也是有问题的,比如没有办法检查赋给属性的值是否有效。建议将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作,Python内置的@property装饰器就是负责把一个方法变成属性调用的。
class Person():def __init__(self, age):self._age = age# 访问器 - getter方法@propertydef age(self):return self._age# 修改器 - setter方法@age.setterdef age(self, age):self._age = agedef talk(self):if self._age <= 16:print('%d太小 不跟你玩了.' % self.age)else:print('%d 成年了, hi.' % self.age)def main():person = Person( 12)person.talk()person.age = 22person.talk()#person.name = 'liu' # AttributeError: can't set attributeif __name__ == '__main__':main()
静态方法和类方法
前面例子中定义的方法都是对象方法,也就是说这些方法都是发送给对象的消息。实际上写在类中的方法并不需要都是对象方法。
静态方法:是一种不会自动接收类(cls)或实例(self)作为第一个参数的方法。它可以通过类直接调用,而不需要类的实例。静态方法通常用于实现与当前类的对象状态无关的功能,或者提供一些通用的工具函数。
class Car:@staticmethod #静态方法通过装饰器@staticmethod定义,不接受类或实例作为第一个参数def create_car(car_type):if car_type == 'sedan':return Sedan('sedan 时速 ')elif car_type == 'suv':return SUV('suv时速')class Sedan:def __init__(self, type):# ... Sedan特有的属性和方法self._type=typedef run(self):print(self._type, "99km/h")class SUV:def __init__(self, t):# ... SUV特有的属性和方法self._type=tdef run(self):print(self._type,"129km/h")
Car.create_car('suv').run()
Car.create_car('sedan').run()
类方法:类方法的定义是在类的定义内部使用@classmethod装饰器的函数。类方法的第一个参数是类本身作为一个实例,通常用cls作为参数名。
class MyClass:def __init__(self, value):self.value = value@classmethoddef create(cls, value):return cls(value)# 使用定义的工厂方法创建类的实例
instance1 = MyClass.create(10)
instance2 = MyClass.create(20)
print(instance1.value) # 输出: 10
print(instance2.value) # 输出: 20