面向对象: 将问题里面涉及到的角色或者对象抽离出来
面向对象 (Object Oriented,OOP) 的思想对软件开发相当重要,它的概念和应用甚至已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD 技术、人工智能等领域。面向对象是一种 对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。
面向过程 (Procedure Oriented) 是一种 以过程为中心 的编程思想。
无论是在软件开发还是在实际工作中,深入地理解软件开发的思想都非常有必要。
对一类具有相同属性和方法的抽象集合,对象就是这样的模板创建出来的的一个个对象
对象:属性 + 方法的具体事务
类: 是所有具备相同属性和方法的归纳
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类式抽象的模板,比如Person类是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
# 函数声明
def 函数名(args):
函数体
def add(x, y):
ret = x + y
return ret
class 类名(object): # 大驼峰式命名,每个单词的首字母大写,其它字母小写
类变量 = 值
def 实例方法(self,*args,**kwargs):
pass
举例类:注意 python 里 没有new关键字,可直接实例化: 实例化对象 = 类()
通过类创建对象的过程叫做实例化,且可以创建出无数个类 : 实例化对象 = 类()
每一个类在创建一个实例对象的时候,这个实例对象一定是独一无二的
# 通过类创建对象的过程叫做实例化,且可以创建出无数个类 : 实例化对象 = 类()
class Person:
"""
这是一个人类
"""
pass
p1 = Person() # 实例化对象 p1,并开辟 内存空间
print(p1)
# <__main__.Person object at 0x0000021EC69EA588>
p2 = Person() # 实例化对象 p2,并开辟 内存空间
print(p2)
# <__main__.Person object at 0x0000021EC69EA550>
类与实例化对象内存空间如何存储的?
# 对对象的操作都是通过 . 来完成
创建一个人类,并实例化创建出两个实例对象
class Person:
"""
这是一个人类
"""
pass
p1 = Person() # 实例化对象 p1,并开辟 内存空间
print(p1)
# <__main__.Person object at 0x0000021EC69EA588>
p2 = Person() # 实例化对象 p2,并开辟 内存空间
print(p2)
# <__main__.Person object at 0x0000021EC69EA550>
p1.name = 'RedHat' # 将 name = 'RedHat' 存到 p1 的内存空间里去
p2.name = 'CentOS' # 将 name = 'CentOS' 存到 p2 的内存空间里去
print(p1.name)
print(p2.name)
输出:
RedHat
CentOS
# p1 表示 指向 p1 实例对象的那段内存空间,.name 表示查 name 属性的 值
p1.name = 'redhat'
print(p1.name)
输出:
redhat
del p1.name # p1 指向实例对象内存空间的属性name值被删除
print(p1.name)
# AttributeError: 'Person' object has no attribute 'name'
这种方式需要先实例化再赋值实例变量,如何能在初始化对象的时候就将实例变量赋值好呢?这就要利用到类的魔法方法中最重要的构造方法了。
一个类有很多的魔法方法,其中之一构造方法,类实例化过程中一定执行的方法
魔法方法定义规范:双下划线开头结尾的方法
创建一个类的同时会开辟一段内存空间,类实例化对象会给实例对象开辟一段实例对象的内存空间。
# 定义Person类
class Person(object):
"""
魔法方法(双下划线开头结尾的方法):构造方法,类实例化过程一定执行的方法
"""
def __init__(self, name, age):
# self的功能: 完成实例对象(self)的属性初始化工作
print(name)
print(age)
# 实例化Person类的实例对象:类实例对象 = 类名(实例化参数)
alex = Person('alex', 18)
当程序走到初始化创建对象Person()时,python解释器在传递实参时,默认传的第一个参数是该实例对象(实例对象的内存空间),然后通过构造方法中的 self 来接收,即 self --> 指向实例对象的内存空间,也就是实例对象。
self的功能: 完成实例对象(self)的属性初始化工作
# 定义Person类
class Person(object):
def __init__(self, name, age):
print(name)
print(age)
print(self)
# 实例化Person类的实例对象:类实例对象 = 类名(实例化参数)
alex = Person('alex', 18)
print(alex)
# 定义Person类
class Person(object):
"""
魔法方法(双下划线开头结尾的方法):构造方法,类实例化过程一定执行的方法
"""
def __init__(self,name,age):
# self的功能: 完成实例对象(self)的属性初始化工作
self.name = name # 实例变量|实例属性
self.age = age # 实例变量|实例属性
# print(name)
# print(age)
# print(self)
print(id(self))
# 实例化Person类的实例对象:类实例对象 = 类名(实例化参数)
alex = Person('alex', 18)
yuan = Person('yuan', 22)
yuan.gender = "male" # 此时yuan有3个属性,alex只有默认的2个属性,他们的属性名都相同,属性值是各自的
print(id(alex))
# 2513648002440
# 2513648002384
# 2513648002440
注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,各种属性绑定到self 进行初始化赋值,因为 self 就执向创建的实例对象本身。
实例方法或者叫对象方法,指的是我们在类中定义的普通方法。只有实例化对象之后才可以使用的方法,该方法的第一个形参接收的一定是对象本身。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 实例对象调用实例方法时一定会将实例对象自身作为第一个参数传到方法中,所以实例方法定义时一定要有一个固定的形参,默认叫self
def sleep(self): # self: 调用方法的实例对象
print("{} is sleeping...".format(self.name))
def run(self): # self: 调用方法的实例对象
print("{} is running...".format(self.name))
yuan = Person("yuan", 18)
alex = Person("alex", 20)
# 实例对象调用属性或者方法是通过
# 对象.属性
# 对象.方法()
# alex.sleep()
# yuan.sleep()
实例对象调用实例方法时一定会将实例对象自身作为第一个参数传到方法中,所以实例方法定义时一定要有一个固定的形参,默认叫self
实例对象搜索属性顺序:实例对象.名称 名称查找顺序: 实例对象自身实例空间-----> 类空间
实例对象.名称 名称查找顺序: 实例对象自身实例空间-----> 类空间
如下,找到实例对象自身的即返回,就不再去类里边找了
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 实例对象调用实例方法时一定会将实例对象自身作为第一个参数传到方法中,所以实例方法定义时一定要有一个固定的形参,默认叫self
def sleep(self): # self: 调用方法的实例对象
print("{} is sleeping...".format(self.name))
def run(self): # self: 调用方法的实例对象
print("{} is running...".format(self.name))
yuan = Person("yuan", 18)
alex = Person("alex", 20)
alex.sleep = 1
# 实例对象调用属性或者方法是通过
# 对象.属性
# 对象.方法()
alex.sleep() #报错, 'int' object is not callable
yuan.sleep()
实例属性存在于实例对象空间内,实例方法存在于类空间
class Teacher:
# 属性初始化
def __init__(self,name, age):
# self:实例对象,name,age是实例对初始化的属性,所以叫实例属性,也叫实例变量
self.name = name
self.age = age
# 实例方法:实例对象调用的方法,self就是调用者本身
def teach(self):
print(self.name)
def dayi(self):
pass
# t1,t2,t3是实例对象
t1 = Teacher("yuan", 18)
t2 = Teacher("alex", 23)
t3 = Teacher("xiaobo", 33)
t1.teach()
print(id(t1.teach)) # 2785606313416
print(id(t2.teach)) # 2785606313416
上边两id相等的原因是:实例方法存在类空间内,避免相同的内存复用,从而节省内存。而实例属性存在实例对象空间内,因为各自的属性是不同的。
总结:面向对象就是:把数据及对相应数据的操作方法放在一起,作为一个相互依存的整体—对象。对同对象抽象出其共性,形成类。
python中一切皆对象,为啥这样说呢?
我们之前学习过的字符串,列表,字典等等数据都是一个个的类,我们用的所有数据都是一个个具体的实例对象。
区别就是,那些类是在解释器上注册好的,而现在我们学习的是自定义类,但语法使用都是相同的。所以,我们自定义的类实例对象也可以和其他数据对象一样可以进行传参、赋值等操作。
class Weapon:
def __init__(self,name,av,color):
self.name=name
self.av=av
self.color=color
jiguangqiang=Weapon("激光枪",100,"red")
class Hero:
def __init__(self,name,sex,hp,ce,weapon,level=2,exp=2000,money=10000): # 类必不可少的方法,用于实例化
self.name=name # 英雄的名字
self.sex=sex # 英雄的性别
self.hp=hp # 英雄生命值
self.level=level # 英雄的等级
self.exp=exp # 英雄的经验值
self.money=money # 英雄的金币
self.weapon=weapon # 英雄的武器
alex=Hero("yuan","male",100,80,jiguangqiang)
print(alex.weapon.color)
# s = "hello"
s = str("hello wolrd")
print(s.startswith("h"))
print(s.split("l"))
print(s.upper())
l=[1,2,3]
l=list([1,2,3])
l.append("i")
l.reverse()
l.remove("r")
def foo():
pass
print(foo.__name__) # "foo"
time.sleep(1)
实例对象和类对象可以获取类属性,但是只有类对象才能修改类属性
class Student(object):
# 类属性
school = '清北'
def __init__(self, name, score):
self.name = name
self.score = score
def sleep(self):
pass
# 实例对象:实例属性(name, score) + 实例方法(sleep)
sdt = Student('alex', 45)
# 类属性的获取方式
# 1、 通过类对象直接调用
print(Student.school) # 清北
# 2、 通过实例对象直接调用
print(sdt.school) # 清北
实例对象和类对象可以获取类属性,但是只有类对象才能修改类属性
class Student(object):
# 类属性
school = '清北'
def __init__(self, name, score):
self.name = name
self.score = score
def sleep(self):
pass
# 实例对象:实例属性(name, score) + 实例方法(sleep)
sdt = Student('alex', 45) # 开辟实例对象对应一段内存空间
sdt02 = Student('alex', 45) # 开辟实例对象对应一段内存空间
# 类属性的获取方式
# 1、 通过类对象直接调用
print(Student.school) # 清北
# 2、 通过实例对象直接调用
print(sdt.school) # 清北
sdt.school = '复旦大学'
print(Student.school) # 清北
print(sdt.school) # 复旦大学
print(sdt02.school) # 清北
定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
像这种计算类,self 是必传参数,但是实例方法又根本用不到 self 参数,而且调用也是基于实例对象调用,针对此情况,我们可以通过 静态方法来解决
class Cal(object):
def add(self, x, y):
print(x+y)
def mul(self, x, y):
print(x*y)
c = Cal()
c.add(1, 2)
# 3
class Cal(object):
def add(x, y):
print(x+y)
def mul(x,y):
print(x*y)
c = Cal()
c.add(1, 2)
一般情况没有 self 参数 肯定会报错
c.add(1, 2)
TypeError: add() takes 2 positional arguments but 3 were given
如何解决呢?
不需要 self 参数的方法,可以使用装饰器@staticmethod,即为 静态方法,实例化对象调用不传self参数也不会报错,可以正常执行
class Cal(object):
@staticmethod
def add(x, y):
print(x+y)
@staticmethod
def mul(x, y):
print(x*y)
c = Cal()
c.add(1, 2)
# 3
总结:简单来说,静态方法就是不适用 self 参数的 普通函数,内置在我们的对象里边的方法。
类方法:可以由类对象和实例对象调用
定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
调用:类对象或实例对象都可以调用。
# 类方法: 可以由类对象和实例对象调用
class Student(object):
# 类属性
number = 88
@classmethod
def foo(cls): # cls 参数,当类对象调用时,解释器会自动传递类对象cls
print(cls) # 类对象
print(cls == Student)
print(cls.number)
Student.foo()
输出:
<class '__main__.Student'>
True
88
类对象能调用的,实例对象也能调用(实例对象空间找不到则去类空间找)
# 类方法: 可以由类对象和实例对象调用
class Student(object):
# 类属性
number = 88
@classmethod
def foo(cls): # cls 参数,当类对象调用时,解释器会自动传递类对象cls
print(cls) # 类对象
print(cls == Student)
print(cls.number)
# Student.foo()
s1 = Student()
s1.foo()
输出:
<class '__main__.Student'>
True
88
# 类方法: 可以由类对象和实例对象调用
class Student(object):
# 类属性
number = 88
@classmethod
def foo(cls): # cls 参数,当类对象调用时,解释器会自动传递类对象cls
print(cls) # 类对象
print(cls == Student)
print(cls.number)
@classmethod
def set_number(cls, num):
cls.number = num
print(cls.number)
# Student.foo()
#
s1 = Student()
s1.foo()
s1.set_number(100)
s1.foo()
输出:
<class '__main__.Student'>
True
88
100
<class '__main__.Student'>
True
100
拒绝 硬编码, 可用性高, 通过实例方法也能修改类变量,但此举称之为硬编码,当类名发生改变时,就必须修改 内部代码了
# 类方法: 可以由类对象和实例对象调用
class Student(object):
# 类属性
number = 88
@classmethod
def foo(cls): # cls 参数,当类对象调用时,解释器会自动传递类对象cls
print(cls) # 类对象
print(cls == Student)
print(cls.number)
@classmethod
def set_number(cls, num):
cls.number = num
print(cls.number)
def set_number2(self, num): # 通过实例方法也能修改类变量,但此举称之为硬编码,当类名发生改变时,就必须修改 内部代码了
Student.number = num
print(Student.number)
# Student.foo()
#
s1 = Student()
# s1.foo()
s1.set_number(100)
# s1.foo()
s1.set_number2(200)
100
200
对象: 属性 + 方法
类 : 将具有相同属性和方法的事务抽象出来的概念
语法: 函数: 封装的语句
类: 封装的属性以及操作属性的方法
class 类名:
类变量
def __init__(self,v1,v2): # 构造方法: 初始化实例属性
self.属性1=v1
self.属性2=v2 # 实例属性(实例变量)指的是属性1,属性2 self是实例对象
def 实例方法名(self,*args):
pass
@classmethod # 类方法可以由类对象以及实例对象调用
def foo(cls) :
print(cls) # cls指的类对象
@staicmethod
def bar(): # 静态方法可以由类对象以及实例对象调用,比如 类名().bar()
pass
初始化:
实例对象=类名(初始化参数) # 默认执行构造方法
实例对象.实例方法() # 解释器默认将该实例对象作为第一个实参传给self
class Student:
school = "OB"
def __init__(self,v1,v2):
self.name = v1
self.age = v2
def study(self,num):
print(self.name)
yuan=Student("yuan",12)
yuan.study(12)
***** : 实例对象.变量 查找顺序 : 优先查找实例对象自己的空间----》实例空间没找到再去类对象空间
实例对象.变量 查找顺序 : 优先查找实例对象自己的空间----》实例空间没找到再去类对象空间
对象:
属性:
方法: