2021-02-02 Python面向对象

Python 面向对象

在这里插入图片描述

面向过程 V.S. 面向对象

面向过程(procedure oriented):关注程序的逻辑流程,按照步骤进行
面向对象(object oriented):关注软件中对象之间的关系
简单数据 -> 数组 -> 结构体 -> 对象
将不同类型的数据、方法放到一起,就是对象
面向对象可以帮助从宏观上把握、分解整个系统,具体到实现部分的方法,仍然需要面向过程的思路去处理

类的定义

类定义数据类型的属性(数据)和方法(行为),也就是说,类将行为和状态打包在一起
从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态)
类名一般首字母大写,多个单词采用驼峰原则
在这里插入图片描述

class Student
#	def__init__(self):
#			self.name = "Jason"
#			self.score = 60
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print("{0}'s score is {1}".format(self.name, self.score))

Student1 = Student("Jason", 80)  # 调用构造方法
Student1.print_score()

object 根类

通过类的方法mro()或者类的属性__mro__可以输出这个类的继承层次结构

class A:pass
class B(A):pass
class C(B):pass
print(C.mro())
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

object 类是所有类的父类,因此所有的类都有object 类的属性和方法

dir()查看对象属性

方法,实际上也是属性。只不过这个属性的类型是“method”而已
例子:
age <class ‘int’>
name <class ‘str’>
say_age <class ‘method’> #方法

构造函数

类是抽象的,也称之为“对象的模板”
通过类这个模板,创建类的实例对象,然后使用类定义的功能。
一个Python 对象包含如下部分:

  1. id
  2. type
  3. value
    • 属性(attribute)
    • 方法(method)
      创建对象,我们需要定义构造函数__init__()方法,用于执行“实例对象的初始化工作”,即对象创建后,初始化当前对象的相关属性,无返回值
      第一个参数固定,必须为:self(可以修改),self指的就是刚建好的实例对象
      通过类名调用构造函数,创建新的对象(方法共用)
      Python 中的self 相当于C++中的self 指针,JAVA 和C#中的this关键字

实例属性 & 实例方法

self.实例属性名 = 初始值
实例方法也可看作属性
def 方法名(self [, 形参列表])
函数体

调用:对象.方法名([实参列表])
调用实例方法时,不需要也不能给self 传参,self 由解释器自动传参
在这里插入图片描述
其他操作:

  1. dir(obj)可以获得对象的所有属性、方法
  2. obj.__dict__ 对象的属性字典
  3. pass 空语句
  4. isinstance(对象,类型) 判断“对象”是不是“指定类型”

类对象、类属性、类方法、静态方法

类对象

当解释器执行class语句时,就会创建一个类对象(<class ‘type’>)

类属性(类变量)

class 类名:
类变量名= 初始值
或通过:“类名.类变量名”来读写

class Student:
    age = 18  # 类属性

    def __init__(self, name, score):
        self.name = name  # 实例属性
        self.score = score  # 实例属性

    def print_score(self):  # 实例方法
        print("{0}'s score is {1}".format(self.name, self.score))


Student1 = Student("Jason", 80)  # 调用构造方法
Student1.print_score()

在这里插入图片描述

类方法

类方法通过装饰器@classmethod 来定义,格式如下:
@classmethod
def 类方法名(cls [,形参列表]) :
函数体

要点如下:

  1. @classmethod 必须位于方法上面一行
  2. 第一个cls 必须有;cls 指的就是“类对象”本身;
  3. 调用类方法格式:“类名.类方法名(参数列表)”。参数列表中,不需要也不能给cls 传值
  4. 类方法中访问实例属性和实例方法会导致错误
  5. 子类继承父类方法时,传入cls 是子类对象,而非父类对象
静态方法

“静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空
间里面”,需要通过“类调用”
不需要传入cls,可用于定义与类别属性无关的方法
@staticmethod
def 静态方法名([形参列表]) :
函数体

要点如下:

  1. @staticmethod 必须位于方法上面一行
  2. 调用静态方法格式:“类名.静态方法名(参数列表)”。
  3. 静态方法中访问实例属性和实例方法会导致错误
class Student:
	age = 18  # 类属性

	def __init__(self, name, score):
		self.name = name  # 实例属性
		self.score = score  # 实例属性

	@classmethod
	def print_age(cls):
		print(cls.age)

	@staticmethod
	def add(a,b):
		print(a+b)

	def print_score(self):  # 实例方法
		print("{0}'s score is {1}".format(self.name, self.score))


Student1 = Student("Jason", 80)  # 调用构造方法
Student1.print_score()

Student.print_age()
Student.add(10, 20)

在这里插入图片描述

方法

特殊方法

Python 的运算符实际上是通过调用对象的特殊方法实现的,比如:

a = 20
b = 30
c = a+b
d = a.__add__(b)
print("c=",c)
print("d=",d)

运算结果:c= 50,d= 50
常见的特殊方法统计如下:
在这里插入图片描述

__del__方法(析构函数)和垃圾回收机制

__del__方法用于实现对象被销毁时所需的操作
Python的自动垃圾回收机制:当对象没有被引用时(引用计数为0),由垃圾回收器调用__del__方法
也可以通过del语句调用__del__方法

class Student:
    def __del__(self):
        print("销毁对象{0}".format(self))


p1 = Student()
p2 = Student()
del p2
print("程序结束")
销毁对象<__main__.Student object at 0x000001B460741160>
程序结束
销毁对象<__main__.Student object at 0x000001B4605B8460>

Process finished with exit code 0
__call__方法和可调用对象

定义了__call__方法的对象称为可调用对象,即该对象可以像函数一样被调用

class Computation:
    def __call__(self, a, b):
        print("computation result")
        return a + b


test = Computation()
print(test.__call__(10, 20))
print(test(10, 20))
computation result
30
computation result
30

Process finished with exit code 0
Python中的方法没有重载

在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可;方法签名包含3
个部分:方法名、参数数量、参数类型
Python 中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由
可变参数控制,因此,Python 中是没有方法的重载的
如果在类体中定义了多个重名的方法,只有最后一个方法有效 -> 建议:不要使用重名的方法

方法的动态性

Python 是动态语言,可以动态的为类添加新的方法,或者动态的修改类的已有的方法

#测试方法的动态性
class Person:
	def work(self):
		print("努力上班!")
		
def play_game(self):
	print("{0}玩游戏".format(self))
	
def work2(s):
	print("好好工作,努力上班!")
	
Person.play = play_game
Person.work = work2
p = Person()
p.play()
p.work()

特殊属性

在这里插入图片描述

属性和方法命名总结

  • _xxx:保护成员,不能用“from module import * ”导入,只有类对象和子类对象能访问这些成员
  • __xxx__:系统定义的特殊成员
  • __xxx: 类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但,在类外部可以通过“对象名. _类名__xxx”这种特殊方式访问,Python 不存在严格意义的私有成员)
    方法和属性都遵循上面的规则

类编码风格

  1. 类名首字母大写,多个单词之间采用驼峰原则
  2. 实例名、模块名采用小写,多个单词之间采用下划线隔开
  3. 每个类,应紧跟“文档字符串”,说明这个类的作用
  4. 可以用空行组织代码,在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类

面向对象的三大特征

  • 封装(隐藏)
    通过私有属性和方法的方式实现封装
  • 继承
    让子类具有父类的特性,提高了代码的重用性
  • 多态
    多态是指一个方法的调用,由于对象不同,会产生不同的行为

封装(私有属性和私有方法)

  1. 通常,两个下划线开头的属性是私有的(private),其他为公共的(public);
  2. 类内部可以访问私有属性(方法);
  3. 类外部不能直接访问私有属性(方法);
  4. 类外部可以通过“_类名__私有属性(方法)名”访问私有属性(方法);
    方法本质上也是属性
# test private feature

class Student:

    def __init__(self, name, age):
        self.name = name
        self.__age = age

    def __show(self):
        print("The student's name is {0}.".format(self.name))
        print("The student's age is {0}.".format(self.__age)) # 内部可以调用

Student1 = Student("Jason", 18)
print(Student1.name)  # 可直接从外部访问
print(Student1._Student__age)  # 不可直接从外部访问
Student1._Student__show()  # 私有方法
@property 装饰器

@property可以将一个方法的调用方式变成“属性调用”,增加读写操作(getter,setter)
对于某一个属性,我们可以直接通过:emp1.salary = 30000
但是,这种做法不安全,需要增加限制,要通过getter、setter 方法来处理

# get & set 方法
class Student:

    def __init__(self, name, age):
        self.name = name
        self.__age = age

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if 0 < age <= 18:
            self.__age = age
        else:
            print("Age out of range!")

    @property
    def __show(self):
        print("The student's name is {0}.".format(self.name))
        print("The student's age is {0}.".format(self.__age))  # 内部可以调用


Student1 = Student("Jason", 18)
print(Student1.name)  # 可直接从外部访问
print(Student1._Student__age)  # 不可直接从外部访问
Student1.get_age()
Student1.set_age(22)
Student1.set_age(15)
# Student1._Student__show()  # 私有方法
Student1._Student__show  # 装饰器调用
# getter setter of property

class Student:

    def __init__(self, name, age):
        self.name = name
        self.__age = age

    @property  # 属性调用
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if 0 < age <= 18:
            self.__age = age
        else:
            print("Age out of range!")


Student1 = Student("Jason", 18)
Student1.age = 20

继承

如果一个新类继承自一个设计好的类,就直接具备了已有类的特征,就大大降低了工作难度
Python 支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:
class 子类类名(父类1[,父类2,…]):
类体

如果在类定义中没有指定父类,则默认父类是object 类,也就是说,object 是所有类的父类,里面定义了一些所有类共有的默认实现,比如:__new__()
定义子类时,必须在其构造函数中调用父类的构造函数,调用格式如下:
父类名.__init__(self, 参数列表)

# object -> person -> student

class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.__gender = gender

    def get_age(self):
        print("Age is {0}.".format(self.age))


class Student(Person):

    def __init__(self, name, age, gender, grade):
        Person.__init__(self, name, age, gender)  # 不然解释器不会调用
        self.grade = grade


Student1 = Student("Jason", 18, "boy", 80)
Student1.get_age()
print(Student1._Person__gender)  # 父类私有属性的调用
类成员的继承和重写
  1. 成员继承:子类继承了父类除构造方法之外的所有成员
  2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
重写__str__( )方法

object 有一个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数str(),经常用于print()方法,

class Person:
	def __init__(self,name,age):
		self.name = name
		self.__age = age
	def __str__(self):
		'''将对象转化成一个字符串,一般用于print 方法'''
		return "名字是:{0},年龄是{1}".format(self.name,self.__age)

p = Person("Jason",18)
print(p)
多重继承

Python支持多重继承,一个子类可以有多个直接父类
尽量避免使用造成复杂的类的整体层次
在子类没有指定父类名时,解释器将
“从左向右”按顺序搜索 -> MRO (Method Resolution Order):方法解析顺序,获得“类的层次结构”

super( )获得父类的定义
class A:
	def say(self):
		print("A: ",self)
		print("say AAA")
		
class B(A):
	def say(self):
		#A.say(self) 调用父类的say 方法
		super().say() #通过super()调用父类的方法
		print("say BBB")
组合

“is-a”关系,我们可以使用“继承”,从而实现子类拥有的父类的方法和属性,例如:MobilePhone is an electrical device
“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性,例如:MobilePhone has a CPU

#组合测试
class MobilePhone:
	def __init__(self,cpu,screen):
		self.cpu = cpu
		self.screen = screen
		
class CPU:
	def calculate(self):
		print("计算")

class Screen:
	def show(self):
		print("显示画面")

c = CPU()
s = Screen()
m = MobilePhone(c,s)
m.cpu.calculate() #通过组合,我们也能调用cpu 对象的方法,相当于手机对象间接拥有了“cpu 的方法”
m.screen.show()

多态

同一个方法调用由于对象不同可能会产生不同的行为

  1. 多态是方法的多态,属性没有多态
  2. 多态的存在有2 个必要条件:继承、方法重写
class Animal:
	def shout(self):
		print("动物叫了一声")
		
class Dog(Animal):
	def shout(self):
		print("小狗,汪汪汪")
		
class Cat(Animal):
	def shout(self):
		print("小猫,喵喵喵")
		
def animalShout(a):
	if isinstance(a,Animal):
		a.shout() #传入的对象不同,shout 方法对应的实际行为也不同
		
animalShout(Dog())
animalShout(Cat())

isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()
isinstance() 与 type() 区别:

  • type() 不会认为子类是一种父类类型,不考虑继承关系
  • isinstance() 会认为子类是一种父类类型,考虑继承关系
    如果要判断两个类型是否相同推荐使用 isinstance()
    语法
    isinstance(object, classinfo)

设计模式

设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法

工厂模式

工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制

class CarFactory:
	def createCar(self,brand):
		if brand == "Benz":
			return Benz()
		elif brand == "BMW":
			return BMW()
		else:
			return "未知品牌"
class Benz:
	pass
class BMW:
	pass
class BYD:
	pass
	
factory = CarFactory() # 首先创建统一管理的工厂类
c1 = factory.createCar("Benz") # 在工厂类下创建品牌

单例模式

单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一
个访问该实例的全局访问点
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较
多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久
驻留内存中,从而极大的降低开销
单例模式有多种实现的方式,推荐重写__new__()的方法

__obj = None
__init_flag = True
	def __new__(cls, *args, **kwargs):
		if cls.__obj == None: # 只在实例不存在的时候才创建
			cls.__obj = object.__new__(cls)
		return cls.__obj

def __init__(self,name):
	if MySingleton.__init_flag: # 只初始化一次 否则每次调用都会重新初始化
		print("init....")
	self.name = name
	MySingleton.__init_flag = False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值