python-面向对象
如果只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护的,那还是用面向对象最方便了。
- 面向过程:根据业务逻辑从上到下写代码
- 面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程
面向对象介绍
OOP(Object-Oriented Programming)编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率,另外,基于面向对象的程序可以使他人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
世界万物,皆可分类
世界万物,皆为对象
只要是对象,就肯定属于某种品类
只要是对象,就肯定有属性
特性
class类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variable(data))、共同的方法
类(Class) 由3个部分构成
- 类的名称:类名
- 类的属性:一组数据
- 类的方法:允许对进行操作的方法 (行为)
定义一个类,格式如下:
class 类名:
方法列表
说明:
-
定义类时有2种形式:新式类和经典类,class 类名(object)为新式类,class 类名()则为经典类;
-
object 是Python 里所有类的最顶级父类;
-
类名 的命名规则按照"大驼峰命名法";
-
方法的第一个参数一般是self,表示实例对象本身,当然了可以将self换为其它的名字,其作用是一个变量 这个变量指向了实例对象
object对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之间有共性,亦有不同
python中,可以根据已经定义的类去创建出一个或多个对象。
创建对象的格式为:
对象名1 = 类名()
对象名2 = 类名()
......
class Dog: def __init__(self,name): self.name = name def bulk(self): # self 是谁? 哪个对象调用了这个方法 self就是哪个对象 print("%s: wang wang wang"%self.name) # 在类的外面 使用的是对象名.属性名;在类的实例方法内部 使用的是 self.属性名 (self == 调用这个对象方法的对象) d1 = Dog("haha") #实例化 d2 = Dog("xixi") d3 = Dog("gaga") d1.bulk() d2.bulk() d3.bulk()
对象名 和 self 本身就是一个人
注意:
1、在类内部获取 属性 和 实例方法,通过self获取;
2、在类外部获取 属性 和 实例方法,通过对象名获取。
3、如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址;
4、但是实例方法是所有对象共享的,只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。
封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
多态
太是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父亲的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来,再通过这个抽象的事物,与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在9点钟开始工作,他只要在9点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”,因为“员工”是一个抽象的事物,只要是员工就可以开始工作,他知道这一点就行了,至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
语法
属性
方法
构造函数
- __init__()方法,在创建一个对象时默认被调用,不需要手动调用
- __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
- __init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
析构函数:在实例释放、销毁的时候自动执行的,通常用于做一些收尾工作,如关闭一些数据库链接,打开的临时文件
创建对象后,python解释器默认调用__init__()方法;
当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法
程序执行完后,python会销毁对象,python中是自动内存管理
-
当有变量保存了一个对象的引用时,此对象的引用计数就会加1;
-
当使用del() 删除变量指向的对象时,则会减少对象的引用计数。如果对象的引用计数不为1,那么会让这个对象的引用计数减1,当对象的引用计数为0的时候,则对象才会被真正删除(内存被回收)。
class Hero(object): # 魔法方法: 以两个下划线开头 在以两个下划线结尾的方法 # 在特殊的情况下 被python调用 (程序员也可以手动调用) def __init__(self, name): # 构造方法 # 设置属性的值 self.name = name def __str__(self): # 输出一个字符串(追踪对象属性信息变化) return "名字:%s" % self.name def __del__(self): # 监听对象销毁后会走的方法 print("再见") # # 创建一个对象 # gailun = Hero("盖伦") # # 程序员杀死对象 # del gailun # # input("停在这里") # # # python中是自动内存管理 # 创建一个对象 gailun = Hero("盖伦") gailun1 = gailun gailun2 = gailun # 引用计数问题 del gailun del gailun1 del gailun2 input("停在这里")
class Role(object): def __init__(self,name,role,weapon): self.name = name self.role = role self.weapon = weapon def __del__(self): # 监听对象销毁后会走的方法 #析构函数 print("%s 彻底死了。。。。"%self.name) def got_shot(self): print("ah...,I got shot...") def buy_gun(self,gun_name): print("%s just bought %s"%(self.name,gun_name)) r1 = Role('Alex','police','AK47') r1.buy_gun("AK47") r1.got_shot() del r1 r2 = Role('Jack','terrorist','B22') r2.got_shot() # 显示如下: # Alex just bought AK47 # ah...,I got shot... # Alex 彻底死了。。。。 删除r1后会执行 # ah...,I got shot... # Jack 彻底死了。。。。 程序退出后,也会执行
私有方法,私有属性
1、类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
2、类的私有属性 和 私有方法,都不会被子类继承,子类也无法访问;
3、私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用。
- Python中没有像C++中 public 和 private 这些关键字来区别公有属性和私有属性。
- Python是以属性命名方式来区分,如果在属性和方法名前面加了2个下划线'__',则表明该属性和方法是私有权限,否则为公有权限。
class Role(object): def __init__(self,name,role,weapon,life_value=100,money=15000): self.name = name self.role = role self.weapon = weapon self.__life_value = life_value #私有属性,加两个下划线,只能内部访问 self.money = money def show_status(self): print("name:%s weapon:%s life_value:%s"%(self.name,self.weapon,self.__life_value)) def __shot(self): #私有方法,加两个下划线 print("shooting...") def got_shot(self): self.__life_value-=50 print("ah...,I got shot...") r1 = Role('Alex','police','AK47') #print(r1.__life_value) #报错,不能访问私有属性 r1.got_shot() print(r1.show_status()) #访问私有属性,可以这样
class Person(object): def __init__(self): self.name = "小明" self.__age = 20 # 定义一个方法(获取属性的值 一般方法名中使用get) def get_age(self): return self.__age # 定义一个方法(对属性赋值的时候 一般方法名中使用set) def set_age(self, new_age): self.__age = new_age # 间接的修改私有属性的值 和获取私有属性的值 # 对象 xiaoming = Person() print(xiaoming.name) # 01 使用对象调用私有属性 完成打印age的值 print(xiaoming.get_age()) # 02 使用对象设置私有属性的值 xiaoming.set_age(30)
类属性和实例属性
类变量:大家共用的属性,节省开销
实例变量
class Role(object): #定义一个类,class是定义类的语法,Role是类名,(object)是新式类的写法,必须这样写 n = 123 #类变量,存在类的内存里 name = "我是类name" def __init__(self,name,role,weapon,life_value=100,money=15000):#__init__ 传参数用,初始化函数,在生成一个角色时要初始化的一些属性就填写在这里 #构造函数 #在实例化时做一些类的初始化工作 #实例化时,括号里的self就相当于r1,r1这个变量也存到里内存里 self.name = name #实例变量(静态属性),作用域就是实例本身 self.role = role self.weapon = weapon self.life_value = life_value self.money = money def shot(self): #类的方法,功能(动态属性) # 实例化时,在类的内存里共有的,上面那些属性是各一份 print("shooting...") def got_shot(self): print("ah...,I got shot...") def buy_gun(self,gun_name): print("%s just bought %s"%(self.name,gun_name)) print(Role.n) #不实例化就可以直接调用 r1 = Role('Alex','police','AK47') #把一个类变成一个具体对象的过程叫 实例化(初始化一个类,造了一个对象) r1 = Role(r1,'Alex','police','AK47') r2 = Role('Jack','terrorist','B22') #r2又叫Role的实例 r1.buy_gun('AK47') #Role.buy_gun(r1,'AK47') r2.got_shot() #Role.got_shot(r2) r1.name = "haha" r1.bullet_prove = True #可以添加新的属性 print(r1.n,r1.name,r1.bullet_prove) print(r2.n,r2.name) #先找实例本身,实例本身没有再去类里找
print(r1.weapon) del r1.weapon #删掉这个属性 print(r1.weapon) #再执行报错 r1.n = "改类变量" #相当于r1的内存里加了个n = "改类变量" print("r1:",r1.n) #r1: 改类变量 print("r2:",r2.n) #不影响r2,r2: 123 Role.n = "ABC" print("r1:",r1.n) #不影响r1,r1: 改类变量 print("r2:",r2.n) #r2: ABC
class Person(object): def __init__(self, name): self.name = name def eat(self): print("人会吃饭") print("="*10) # 内存地址问题 xiaoming = Person("小明") # print(id(xiaoming)) # print(id(xiaoming.name)) print(id(xiaoming.eat())) xiaohong = Person("小红") # print(id(xiaohong)) # print(id(xiaohong.name)) print(id(xiaohong.eat())) """ 对象需要占用地址(和对象属性地址不同) 表达的是不同的人 对象的属性也要占用地址(和对象的地址不同) 不同的人属性特征是不同的 # 不同的对象 但是调用的对象方法的id(地址相同)相同(方法相同 因为方法中有一个形参self 可以区分是哪个对象调用的) """
如果需要在类外修改类属性
,必须通过类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性
,这种方式修改的是实例属性
,不会影响到类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性
,除非删除了该实例属性
。
# 自定义一个类 class Person(object): # 国家 country = "中国" def __init__(self, name, age): # 实例属性(对象属性) self.name = name self.age = age xiaoming = Person("小明", 20) # 使用实例属性: 对象名.实例属性名 print(xiaoming.name) # 修改实例属性: 对象名.实例属性名 = 值 xiaoming.name = "小明明" print(xiaoming.name) # 使用类属性 # 01: 类名.类属性名 print(Person.country) # 02: 对象名.类属性名 xiaoming = Person("小明", 20) print(xiaoming.country) # 修改类属性 # 01: 类名.类属性名 = 值 Person.country = "中华" print(Person.country) # 02: 不存在(对象名.类属性名 = 值) xiaoming = Person("小明", 20) # python认为你是给对象设置实例属性(只是和类属性名相同而已) xiaoming.country = "中华" print(xiaoming.country) print(Person.country) # 关于类属性 内存问题 xiaoming1 = Person("小明1", 20) xiaoming2 = Person("小明2", 20) # 类属性python只会开辟一份内存(他是代表这个类的属性这个类python中只有一个) print(id(xiaoming1.country)) print(id(xiaoming2.country)) print(id(Person.country)) # 这三个值一样 # 类属性好处??? # 为了节约内存 # 为了后期业务需求更改 可以提高开发效率
类方法和静态方法
类方法
是类对象所拥有的方法,需要用修饰器@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。
class Person(object): # 类属性(私有) __country = "中国" # 定义类方法 # 获取私有类属性的值 # cls = 类名 # 修饰器 @classmethod def get_country(cls): return cls.__country # 修改私有类属性的值 @classmethod def set_country(cls, new_country): cls.__country = new_country # 01: 类名.类方法(调用类方法) print(Person.get_country()) Person.set_country("中华") print(Person.get_country()) # 02: 对象名.类方法(调用类方法) xiaoming = Person() # 调用类方法 print(xiaoming.get_country()) xiaoming.set_country("中华") print(xiaoming.get_country())
静态方法
需要通过修饰器@staticmethod
来进行修饰,静态方法不需要多定义参数,可以通过对象和类来访问。
# 自定义类 class Person(object): __country = "中国" # 私有的类属性(国籍) def __init__(self): # 构造方法 self.name = "小明" self.__age = 20 # 私有属性 # 实例方法(对象方法)获取私有属性 def get_age(self): return self.__age # 实例方法(对象方法)修改私有属性 def set_age(self, new_age): self.__age = new_age # 类方法 @classmethod def get_country(cls): # 获取私有类属性 return cls.__country @classmethod def set_country(cls, new_country): # 修改私有类属性 cls.__country = new_country # 静态方法 @staticmethod def hello(): print("今天天气不错") # 使用静态方法 # 01: 类名.静态方法名 Person.hello() # 02: 对象名.静态方法名 xiaoming = Person() xiaoming.hello() # 实例方法 xiaoming.get_country() Person.get_age(Person()) # 没有意义的
python中类中的方法总结:
- 实例方法(对象方法) -> 场景很多
- 定义格式: def 实例方法名(self):
- 调用格式: 对象名.实例方法名()
- 使用场景: 在方法中需要self
- 类方法-> 对私有类属性取值或者赋值
- 定义格式: @classmethod
def 类方法名(cls):
- 调用格式: 类名.类方法名() 或者 对象名.类方法名()
- 使用场景: 在方法中需要cls(类名)
- 静态方法 -> 一般不用
- 定义格式: @staticmethod
def 静态方法名():
- 调用格式: 类名.类方法名() 或者 对象名.类方法名()
- 使用场景: 在方法中不需要self 也不需要cls
总结:
- 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;
- 实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
- 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用
__str__()方法
class Hero(object): # 构造方法 def __init__(self, name, hp, atk): # 设置属性的值 self.name = name self.hp = hp self.atk = atk # # 打印信息 # def info(self): # print(self.name, self.hp, self.atk) # 实现下父类已有的魔法方法 # 不可以添加参数(形参) # 会返回一个字符串 # 追踪对象属性信息变化 def __str__(self): return "名字:%s 血量:%d 攻击力:%d" % (self.name,self.hp,self.atk) # 悟空 wukong = Hero("悟空", 4000, 400) # wukong.info() # 默认情况下 打印的是对象的16进制地址 # 如果类中实现了__str__方法 如果打印对象名 会输出的是__str__方法中的返回值(字符串) print(wukong) # 名字:悟空 血量:4000 攻击力:400 # __str__一般用于程序员开发调试代码
说明:
- 在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法
- 当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__(self)方法,那么就会打印从在这个方法中 return 的数据
- __str__方法通常返回一个字符串,作为这个对象的描述信息
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步