如今主流的软件开发思想有两种:一个是面向过程,一个是面向对象。面向过程出现得较早,典型代表为c语言,开发中小型项目的效率很高,但是很难适用于如今主流的大中型项目开发场景。面向对象出现得较晚一些,典型代表为java和c++等语言,更加适合用于大型项目开发。两种开发场景各有利弊。
对于面向过程的思想:需要实现一个功能的时候,看重的是开发的步骤和过程,每一个步骤都需要自己亲历亲为,需要自己编写代码,自己来做。
对于面向对象的思想:当需要实现一个功能的时候,看重的并不是过程和步骤,而是关心谁能来帮我做。
# 生活举例
洗衣服
面向过程(手洗);面向对象(机洗)。
面向对象的三大特征:封装性,继承性,多态性。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝上帝眼里世间存在的对象均为对象,不存在的也可以创造出来。
类:就是具有相同属性和功能的一类事物(抽象版本)
一个类可以找到多个对象
对象:就是类的具体表现,是面向对象编程的核心(具体)
某一个具体事物的存在,在现实世界中可以是看得见摸得着的,可以直接使用。
# 类中也有属性,行为两个组成部分,而‘对象’是类的具体实例
# 区分类和对象
类就相当于制造飞机的图纸,用它来进行创建的飞机就相当于对象
类 = 抽象概念的人 对象 = 实际存在的某个人
例如:
水果 ---- 类
苹果 ---- 类
红苹果 ---- 类
红富士苹果 ---- 类
我嘴里吃了一半的苹果 ---- 对象
class 是一个关键字,用法与def相同,定义一个类。类相当于模板,都可以访问到。
类名使用驼峰(刚开始的几篇有提到过)命名风格,首字母大写,私有类可用下划线开头。
类的结果从大方向来说就分为两部分:静态变量和动态方法
class A:
pass
类名,属性(对对象的特征描述),方法 (对象具有的行为)
# 方法的定义格式与之前学过的函数几乎一模一样,区别在于第一个参数必须为self
类名:人Human 属性:身高/年龄/思想 方法:跑/工作/走路
张三今年18岁,身高1.78, 每天早上去跑步。
(类:Person; 属性:name,age,height 方法:run())
路上有一只黑色的狗狗,它的名字叫小黑,看见我就一直叫。
(类:Dog; 属性:name,color 方法:shout())
class Human:
mind = '哈哈' # 类属性、静态属性
# 类名操作静态属性
# __dict__内置函数 类或对象中的所有属性,通过这种方式只能查询,不能增删改
print(Human.__dict__)
print(Human.__dict__['mind'])
# 增删改查类中的单个属性
print(Human.mind)
Human.mind = '嘻嘻' # 修改
# del Human.mind 删除
print(Human.mind) #-- 嘻嘻
Human.walk = '走路' #增加
print(Human.walk) #-- 走路
运行结果:
如果想查询类中所有内容,通过第一种 dict 方法,如果只是操作单个属性,则用万能的点的方式。
对象是从类中出来的,只要是类名+(),这就是一个实例化过程,这样就会实例化一个对象
创建对象的格式为:对象名 = 类名()
实例方法:由对象调用;至少一个 self 参数,执行实例方法时,自动将调用该方法的对象赋值给 self;通俗点说,当创建一个对象时,就是用一个模子,来制造一个实物,就跟做月饼一样。
举例1:
class Human:
mind = '哈哈'
def work(self): self 就表示 当前调用方法的对象自己
print(self) 结果: <__main__.Human object at 0x00000279A1519B70>
print('我在工作')
# 实例化对象, person变量中仍然记录的是 对象在内存中的地址
person = Human()
# 打印对象,则默认打印对象在内存的地址,结果等同于work里的print(self)
print(person)
# 对象操作类中的方法 . 表示选择属性或者方法
person.work() 调用方法时,程序员不需要传递 self 参数
Human().work() 这样操作也可以,但是不推荐,只能单个对象
# 对象查看类中的属性
print(person.mind)
运行结果:
举例2:在方法内通过self获取对象属性
class A():
def test(self):
print('%s 的年龄是 %d' % (self.name,self.age))
a = A()
# self 和 a 指向的是同一个内存地址同一个空间
a.name = '张三'
a.age = 20
a.test()
# 查询对象中所有属性,用‘对象.__dict__’
print(a.__dict__)
运行结果:
# 对象操作对象中的单个操作
给对象设置属性非常地容易,但是不推荐使用,因为对象属性的封装应该封装在类的内部
class A():
def test(self):
print('%s 的年龄是 %d' % (self.name,self.age))
a = A()
# self 和 a 指向的是同一个内存地址同一个空间
a.name = '张三'
a.age = 20
a.test()
# 查询对象中所有属性,用‘对象.__dict__’
print(a.__dict__)
a.job = 'IT' # 增加
print(a.job)
# print(a.job) # 查看实例属性 IT
a.age = 18 # 修改
print(a.age)
运行结果:
此时我若执行‘del‘这条命令,再进行查看被删除的指令,就会报错。
类中的方法一般都是通过类的对象执行的(除去类方法,静态方法外),并且对象执行这些方法都会自动将对象空间传递给方法中的第一个参数 self 。
# self 其实就是类中方法(函数)的第一个位置参数,只不过解释器会自动将调用这个函数的对象传递给 self 。所以咱们把类中的方法的第一个参数默认成 self ,代表这个就是对象。
# 思考:创建对象后再去添加属性有点不合适,有没有简单的办法,可以在创建对象的时候就已经拥有这些属性?
构造放过__init__,具有初始化的作用,也就是当该类被实例化的时候就会自动执行该函数,那么通常就可以把要先初始化的属性放到这个方法里面。
1.python的类里提供的,两个下划线开始,两个下划线结束的方法,就是魔法方法。__init__就是一个魔法方法,通常用来做属性初始化或赋值操作;
2.如果类里面没有写__init__方法,python会自动创建,但不执行任何操作;
3.为了能够完成自己想要的功能,可以自己定义__init__方法;
4.所以一个类里,无论自己是否编写过__init__方法,一定存在__init__方法。
# 当使用类名()创建对象时,会自动执行以下操作
为对象在内存中分配空间 --- 创建对象
为对象的属性设置初始值 --- 初始化方法(init)
class Hero(): # 定义了一个英雄类,可以移动和攻击
# 用来做变量初始化或赋值操作,在类实例化对象的时候,会被自动调用
# __init__(self)中的self参数,不需要开发者传递,python解释器会自动将当前的对象引用传递过去
def __init__(self):
self.name = '泰坦'
self.hp = 200 # 生命值
self.at = 450 # 攻击力
def move(self): # 实例方法
print('%s 正在移动中'% self.name)
def attack(self):
print(f'{self.name}正在遭受攻击,当前生命值为{self.hp},这一招的攻击力时{self.at}')
# 实例化对象,自动调用__init__方法
hero = Hero()
# 只需要调用实例化方法__init__,即可获得英雄的属性
hero.move()
hero.attack()
运行结果:
那么问题来了,一个游戏里有很多的英雄角色,怎样让实例化的每个对象,拥有不同的属性呢?
请接着看:
class Hero(): # 定义了一个英雄类,可以移动和攻击
# 用来做变量初始化或赋值操作,在类实例化对象的时候,会被自动调用
# __init__(self)中的self参数,不需要开发者传递,python解释器会自动将当前的对象引用传递过去
def __init__(self,name,hp,at):
self.name = name
self.hp = hp # 生命值
self.at = at # 攻击力
def move(self): # 实例方法
print('%s 正在移动中'% self.name)
def attack(self):
print(f'{self.name}正在遭受攻击,当前生命值为{self.hp},这一招的攻击力时{self.at}')
# 赋予不同角色不同的属性
hero1 = Hero('大乔',200,100)
hero2 = Hero('小乔',400,200)
# 只需要调用实例化方法__init__,即可获得英雄的属性
hero1.move()
hero1.attack()
hero2.move()
hero2.attack()
运行结果:
注意:
1.在类内部获取属性和实例方法,通过self获取;
2.在类外部获取属性和实例方法,通过对象名获取;
3.如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址;
4.但是,实例方法是所有对象共享的,只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。
类属性属于类,实例属性属于对象
如果想在类外修改类属性,必须通过类对象去引用再进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,并不是类属性。并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除该实例属性。
class B: # 类对象
num = 0 # 类属性,在__init__外部定义。
def __init__(self, name):
self.name = name # 实例属性,在__init__内部定义
def test(self): # 实例方法,调用了实例属性
print('我的名字是%s' % self.name)
b = B('zs') # 通过类创建的对象称为实例对象
b.test()
运行结果:
总结:
实例属性属于各个实例所有,互不干扰;
类属性属于类所有,所有实例共享一个属性;
不要对类属性和实例属性使用相同的名字,否则可能会产生难以发现的错误。
现在,我来重复几个点:
1.类和对象
类:类型,自定义各种类型用于对应现实生活,例如student类,teacter类
对象:变量,类的实例化,用类创建的具体对象
2.__init__(self):构造函数,构造方法
(1)可以包含多个参数,但是必须包含self参数
(2)且self必须为第一个参数
(3)self不需要手动传递参数
3.类的实例化
格式:类名(参数),这里放的参数会传递到__init__构造函数中
类中访问当前对象的属性和方法:通过self
对象访问属性和方法:使用“.”运算符
4.属性和方法
类属性(类变量):在类体中,所有函数外定义的变量
使用时,类名.变量名
实例属性(实例变量):以self.变量名定义的变量
使用时:实例名.变量名,self.变量名
接下来,我来写一下类方法
使用装饰器@classmethod
类属性:
1.就是针对类对象定义的属性
2.用于记录与这个类相关的特征
类方法:
1.就是针对类对象定义的方法
2.在类方法内部可以直接访问类属性,或者调用其他的类方法
语法:
@classmethod
def 类方法名(cls):
pass
介绍:
1.类方法需要使用装饰器 @classmethod 来标识,告诉解释器这是一个类方法
2.类方法的第一个参数应该是cls
由哪一个类调用的方法,方法中的cls就是就是哪一个类的引用
这个参数和 实例方法的第一个参数是self 类似
提示使用其他名称也可以,不敢习惯使用cls
通过类名,调用类方法,调用方法时,不需要传递 cls 参数
在方法内部:
可以使用cls,访问类的属性
也可以通过cls,调用其他的类方法
对于类方法,第一个参数必须是类对象,一般以`cls`作为第一个参数(当然可以用其他名称的变量作为其第 一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和 类对象去访问。
原则上,类方法是将类本身作为对象进行操作的方法。假设有个方法,且这个方法在逻辑上采用类本身作为对 象来调用更合理,那么这个方法就可以定义为类方法。另外,如果需要继承,也可以定义为类方法。
class Person:
age = 20
# 类方法
@classmethod
def human(cls):
return cls.age
p = Person()
print(p.human()) # 可以用过实例对象引用
print(Person.human()) # 可以通过类对象引用
# 对类属性进行修改
class Person:
age = 20
@classmethod
def get_age(cls):
print(cls.age)
@classmethod
def set_age(cls, age):
cls.age = age
p = Person()
p.get_age() # 20
Person.get_age() # 20
p.set_age(18)
p.get_age() # 18
Person.get_age() # 18
运行结果:
# 用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变
1. 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过 cls引用的必定是类对象的属性和方法;
2. 实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个 需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
3. 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用 。
使用装饰器@staticmethod。
在开发时,如果需要在 类 中封装一个方法,这个方法: 既不需要访问实例属性 或者调用实例方法 也不需要访问类属性 或者调用类方法 这个时候,可以把这个方法封装成一个静态方法
语法:
@staticmethod
def 静态方法名():
pass
介绍: 静态方法 需要用 修饰器 @staticmethod 来标识,告诉解释器这是一个静态方法
通过 类名. 调用 静态方法
静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身 没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立 的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
class Dog(object):
@staticmethod
def run():
# 不需要访问实例属性也不需要访问类属性的方法
print('狗狗在跑')
dog = Dog()
dog.run() # 狗狗在跑
运行结果:
class A:
age = 10
@staticmethod
def get_age():
return A.age
a = A()
print(a.get_age())
print(A.get_age())
运行结果:
# 实例方法
方法内部需要访问实例属性,内部可以使用类名. 访问类属性
# 类方法
方法内部只需要访问类属性
# 静态方法
方法内部,不需要访问实例属性和类属性
# 如果方法内部 即需要访问 实例属性,又需要访问 类属性,应该定义成什么方法?
应该定义实例方法,因为,类只有一个,在实例方法内部可以使用类名. 访问类属性
OK,这篇文章就写到这里了,其实这篇文章我没有写多少东西,类这一块,我复习完后很纠结,因为它的内容很多,而且我最近要期末考试,时间根本不够,我估计了一下,我来写的话,5w字都写不完,所以我就只写了一些比较重要的,等3月份的时候再看看有没有时间补充。
下次继续!
送给大家一句话,不要因为别人都在交卷就乱写答案。
最后,祝自己,也祝看到这篇文章的人新的一年快快乐乐!实现自己的目标!
一起向前冲!!!