文章目录
面向过程编程:将程序分解为一系列的步骤,每个步骤都是一个函数,以事物发生过程为主要目标进行编程(什么正在发生)。程序的状态通常由全局变量来维护,而函数则被用来封装可重用的代码块。
函数式编程,函数式的思想:函数内部需要的数据均通过参数的形式传递。强调函数的纯度和不可变性。
面向对象编程,面向对象的思想:将数据和操作数据的方法组合在一起,形成一个对象。每个对象都有自己的状态(data)和行为(methods),并且可以通过调用其他对象的方法来改变自身状态或执行其他操作。面向对象编程的核心概念包括类、对象、继承、封装和多态性。
6.1 类与对象
类与对象的关系即模板与产品:模板只能有一个,但对象可以有很多个
面向对象实现功能的步骤:
- 定义类,在类中定义方法,在方法中去实现具体的功能。
- 实例化类给一个对象,通过对象去调用并执行方法。
名词解释
- 类是对一些具有相同属性特征和行为的事物的分类,它是抽象的。
- 属性:属性是指类或对象中的变量,用于存储对象的特征状态,比如年龄、身高、性别、姓名等都叫做属性。
- 方法:用来描述事物的行为,用函数来完成这些行为。比如说话、走路、吃饭等
- 实例(对象):一个对象即是一个类的实例化后实例,是具体的。一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象可以有不同的属性,能调用类中共同的方法。
- 实例化:把一个类转变为一个对象的过程就叫实例化。
1. 类
我们可以使用关键字 class
定义类,关键字后面紧跟类的名称、分号和类的实现。
1. 类名称首字母大写&驼峰式命名
2. 在类种编写的函数称为方法,每个方法的第一个参数是self(类的方法与普通的函数唯一区别)
3. py3之后默认类都继承object,
旧式类:
class Ab Doct:
pass
新式类:
class Ab Doct(object):
pass
2. 对象
对象 = 类() ,基于类实例化出来”一块内存“,默认里面没有数据;
__init__
初始化方法(也称构造方法),在类实例化过程中会自动执行,可以在内存中初始化一些属性数据。
- 不需要初始化的话,可以不写__init__方法,因为解释器会自动生成一个__init__方法来执行。
- 实例化时,先调用了__new__(cls)方法,创建并返回一个类(cls)的新实例对象,传递给构造方法。
- 封装,就是使用构造方法将属性数据封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
class Stu(object):
def __new__(cls, *args, **kwargs): # __new__方法的第一个参数是类本身,后面的参数是传递给构造方法的参数。
print('no.1')
obj = super().__new__(cls) # 重写需要先调用实现过程,并返回一个类的实例
return obj
def __init__(self): # 第一个参数为self的实例方法,实例化一个新对象时,会自动调用这个方法。全名initialize。
print('no.2')
s1 = Stu()
# no.1
# no.2
3. 类对象
在python中,一切都是对象,当我们定义了一个类,这个类也是对象,叫做类对象。
- 类对象是一个类的实例,它是用来创建对象的模板。类对象可以访问类属性和方法,也可以被用来创建实例对象。
- 对象是类对象的实例,它是由类对象创建出来的。对象可以访问实例属性和方法,也可以访问类属性和方法
- 类对象也具有id,type,value三大特点。类的类型是“class type”
class Stu():
def speak(self):
print('running')
print(Stu, 'and id is:', id(Stu), 'type is:', type(Stu))
S_copy = Stu
print(S_copy, 'and id is:', id(S_copy), 'type is:', type(S_copy))
# <class '__main__.Stu'> and id is: 2150663355640 type is: <class 'type'>
# <class '__main__.Stu'> and id is: 2150663355640 type is: <class 'type'>
6.2 类的成员
在 Python 中,面向对象编程中的成员包括属性和方法。
- 属性是指类或对象中的变量,而方法则是类或对象中的函数。在类中定义的属性和方法可以被该类的所有实例对象所共享。
- 属性和方法可以使用点运算符来访问,语法形式为 对象名.属性名 和 对象名.方法名()。
- Python 还支持访问控制机制,即通过使用下划线来表示成员的可见性,例如单下划线表示该成员是受保护的,双下划线表示该成员是私有的。
1. 类属性与实例属性
- 类属性:定义在类中,但不属于任何实例对象的属性。也叫类变量,属于类对象所有,可以被所有该类的实例对象所共享,可以通过类名直接访问和修改。
- 实例属性:加了self的属性,它隶属于对象而不是类(这些属性是放在实例对象的内存空间里的,而不是类对象的内存空间里),能被不同的对象赋值,对象不同,self指向的内存地址也不同。这个对象的所有方法都可以访问和使用本对象的实例属性。实例属性一般定义在__init__方法里,也可以定义在其他方法里。
class Student():
num = 10 # 定义了一个属性,没有加self,也没有放在方法里边,他是类属性(类变量),属于Student这个类
def __init__(self, name, age): # name,和age两个和普通的函数形参变量一样,用来接收参数。方法执行完被清空。
self.name = name # 加了self,说明这个属性对象可以拿去使用,赋值后放在实例对象的内存空间里。供对象调用
self.age = age
print(self.num) # 当对类变量只做访问不做修改的话,也可以使用self.变量名来访问。
def showinfo(self):
gender = 'male' # 这个变量没加self,就是一普通局部变量,showinfo方法执行完毕,他就没了
print(gender)
print(self.age,self.name) # 调用属性,用self来确定到底是哪一个对象的age属性
# 创建了Student类的两个实例对象stu1和stu2,自动初始化, 需要传入两个属于stu1这个对象的特性属性
stu1 = Student(name='xiaoming', age=12) # 10
# 类创建的对象也拥有类变量的使用权,但是没有修改权,一旦产生同名变量,则会隐藏类变量
print(stu1.num) # 10
# 类属性的修改需要直接基于类,用s1.num会创建一个实例属性。
Student.num = 50
stu2 = Student('lihua', 18) # 50
# 因为不同的对象都会享有他们类的的方法,属性各自赋值,现在测试一下
stu1.showinfo()
# male
# 12 xiaoming
stu2.showinfo()
# male
# 18 lihua
#果然都能调用showinfo方法,打印了male,但是name和age各是各的。
2.实例方法
Python 严格要求实例方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。
实例方法是属于类对象的,也就是说实例方法包含在类对象的内存空间里。通过对象名和点号来访问。
class Stu:
def info_age(self, age): # 实例方法,第一个参数是self,可被此类所有对象共享
self.age = age
def show_age(self):
print(self.age) # 可以访问其他方法里定义的实例属性,因为他和我同属于一个对象
s1 = Stu()
Stu.info_age(s1, 20)
Stu.show_age(s1)
# 常规写成下边两行的形式,但python解释器其实以上面两句代码形式执行。
s1.info_age(18) # 将类对象s1的年龄修改成了18
s1.show_age()
# 20
# 18
3. 方法装饰器
Python内置了两个装饰器函数,使用它们可以把类中函数转换为专用类方法或静态方法。
- classmethod:装饰为类方法。对于类方法来说,习惯上使用cls设置第一个形参,当然也可以使用其他名称。
- staticmethod:装饰为静态方法。无默认实参,如果要在静态方法中访问类的属性,只能通过引用类对象来实现。
def test(): # 普通函数
print(666)
class Test():
def __init__(self):
self.test()
@classmethod # 定义类方法
def test(cls):
print(666)
def ceshi(self):
self.test() # 这里用self.类方法名也可以调用类方法
test()
Test.test()
t1 = Test()
# 666
# 666
# 666
静态方法与普通函数没有本质区别,只是放的位置有差异。普通函数定义在模块级别。静态方法本质上就是:普通函数放在类里边了,也没有类方法的cls参数。静态方法通常用于执行与类相关的操作,例如创建和销毁对象、获取类信息等。
def test(): # 普通函数
print(666)
class Test():
@staticmethod # 类似于普通函数放在类里边
def show():
print(666)
def test(self):
self.show() # 利用self.静态方法名也能调用
Test.show() # 通过类名.静态方法名来访问
t1 = Test()
t1.show()
# 666
# 666
实例方法、类方法和静态方法的区别
- 实例对象可以调用3种方法,类对象只能调用类方法和静态方法。
- 类对象调用实例方法时,实例方法变为普通函数,将失去默认的上下文运行环境。实例方法的上下文运行环境是实例对象,而类方法的上下文运行环境是类对象。
- 访问属性的不同,实例属性只有实例方法可以访问。实例方法也能使用类属性,但是不能修改。静态方法只能通过参数或者类对象间接访问类属性。
3.属性装饰器
静态属性存在一个缺陷:无法对用户的访问进行监控。例如,设置price属性,写入时要求必须输入数字,读取时显示两位小数。
Python内置了property装饰器函数,使用该装饰器可以把一个普通函数转换为函数式属性。这样通过函数的行为对用户的访问进行监控,避免乱操作。
属性的访问包括:读、写、删,对应的装饰器为:@property、@方法名.setter、@方法名.deleter。
在函数的上一行添加@property装饰器,就可以定义函数式属性。在函数式属性中,第一个实参自动被设置为实例对象,一般以self作为形参名,也可以使用其他名称。
当访问函数式属性时,与静态属性的用法相同,使用点语法即可,不需要使用小括号调用属性函数。这种简化的语法形式符合属性的习惯用法。
【示例】设计一个商品报价类,初始化参数为原价和折扣,然后可以读取商品实际价格,也可以修改商品原价,或者删除商品的价格属性。
class Goods(object):
def __init__(self, price, discount=1): # 初始化函数
self.orig_price = price # 原价
self.discount = discount # 折扣
@property
def price(self): # 读取属性函数
new_price = self.orig_price * self.discount # 实际价格=原价*折扣
return new_price
@price.setter
def price(self, value): # 写入属性函数
self.orig_price = value
@price.deleter
def price(self): # 删除属性函数
del self.orig_price
obj = Goods(120, 0.7) # 实例化类
print( obj.price ) # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价
print( obj.price ) # 不存在,将抛出异常
注意:如果定义只读函数式属性,则可以仅定义@property和@price.deleter装饰器函数。
4.构造属性
属性装饰器用法比较烦琐,使用property()函数构造属性可以快速封装,语法格式如下:
class property([fget[, fset[, fdel[, doc]]]])
参数说明如下。
- fget:获取属性值的实例方法。
- fset:设置属性值的实例方法。
- fdel:删除属性值的实例方法。
- doc:属性描述信息。
class Goods(object):
def __init__(self, price, discount=1): # 初始化函数
self.orig_price = price # 原价
self.discount = discount # 折扣
def get_price(self): # 读取属性
new_price = self.orig_price * self.discount # 实际价格=原价×折扣
return new_price
def set_price(self, value): # 写入属性
self.orig_price = value
def del_price(self): # 删除属性
del self.orig_price
# 构造price属性
price = property(get_price, set_price, del_price, "可读、可写、可删属性:商品价格")
obj = Goods(120, 0.7) # 实例化类
print( obj.price ) # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价
print( obj.price ) # 不存在,将抛出异常
5.一些相关的内置函数(BIF)
issubclass(class, classinfo)
方法用于判断参数 class 是否是类型参数 classinfo 的子类。- 一个类被认为是其自身的子类。
classinfo
可以是类对象的元组,只要class是其中任何一个候选类的子类,则返回True
。
class A:
pass
class B(A):
pass
print(issubclass(B, A)) # True
print(issubclass(B, B)) # True
print(issubclass(A, B)) # False
print(issubclass(B, object)) # True
isinstance(object, classinfo)
方法用于判断一个对象是否是一个已知的类型,类似type()
。type()
不会认为子类是一种父类类型,不考虑继承关系。isinstance()
会认为子类是一种父类类型,考虑继承关系。- 如果第一个参数不是对象,则永远返回
False
。 - 如果第二个参数不是类或者由类对象组成的元组,会抛出一个
TypeError
异常。
【例子】
a = 2
print(isinstance(a, int)) # True
print(isinstance(a, str)) # False
print(isinstance(a, (str, int, list))) # True
class A:
pass
class B(A):
pass
print(isinstance(A(), A)) # True
print(type(A()) == A) # True
print(isinstance(B(), A)) # True
print(type(B()) == A) # False
hasattr(object, name)
用于判断对象是否包含对应的属性。True Falsegetattr(object, name[, default])
用于返回一个对象属性值。 不存在报错。setattr(object, name, value)
对应函数getattr()
,用于设置属性值,该属性不一定是存在的。delattr(object, name)
用于删除属性。不存在报错。class property([fget[, fset[, fdel[, doc]]]])
用于在新式类中返回属性值。
class A(object):
bar