一、类的基础概念
类
是一个抽象的概念,可以理解为具有相同属性的一组对象的集合,类的属性有两种
- 属性(类定义中的变量,别名:静态变量,静态属性)
- 方法(类定义中的函数,别名:动态变量,动态属性)
实例
则是类的一个具体的对象,实例唯一操作就是属性引用。
1.1类的定义
以动物(Animal)类为例,Python 提供关键字 class 来声明一个类:
class Animal(其他类名):
pass
其中,Animal 是类名,通常类名的首字母采用大写(如果有多个单词,则每个单词的首字母大写).如果后面紧跟着 (其他类名),表示该类是从某个类继承而来的,所有类默认会继承object 类,但(object)默认不用写.
每个类默认有__dict__方法,可以将类中的属性和方法以字典的形式展现(属性展现格式,属性名:对应的值 方法名:对应的内存地址
)
一般用于查询类的所有属性和方法,如果是操作单个属性和方法使用点号,通过万能的点 可以增删改查类中的单个属性和方法
1.2类的实例化
类的实例化可以得到一个具体的对象。
class Animal():
pass
tiger = Animal()
在创建实例的时候,还可以传入一些参数,以初始化实例,为此,我们需要添加一个 __init__ 方法
class Animal():
def __init__(self, name):
self.name = name
tiger = Animal('t1') #实例化,创建具体的对象
print(tiger.name)
__init__方法就是实例的初始化方法,他的第一个参数永远是self(指实例本身),在创建实例时就会调用初始化方法,传递参数必须与初始化方法对应。
实例化一个对象总共发生了三件事:
- 在内存中开辟了一个对象空间。
- 自动执行类中的__init__方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
- 在__init__ 方法中通过self给对象空间添加属性。
类名称空间和对象空间的关系
从图中可以知道对象之所以能找到类中的属性,是因为有个类对象指针指向类的名称空间.
对象查找属性的顺序:先从对象空间找 ------> 类空间找 ------> 父类空间找 ------->…
类名查找属性的顺序:先从本类空间找 -------> 父类空间找--------> …
上面的顺序都是单向不可逆,类名不可能找到对象的属性。
1.3类自定义方法
除了初始化方法__init__还可以为类添加自定义方法。
class Animal():
def __init__(self, name):
self.name = name
def greet(self):
print('Hello, I am %s.' % self.name)
tiger = Animal('t1')
tiger.greet()
类的自定义方法,其第一个参数也必须是self(指向实例本身),这些方法构建格式与函数极度相似。
类的自定义方法也可以在类定义代码块外先定义,然后在类定义代码块中引用,如下:
def greet(self,y):
print('Hello %s, I am %s'%(y,self.name))
class Animal():
def __init__(self, name):
self.name = name
p=greet
tiger = Animal('t1')
tiger.p('bb')
类的方法,可以被同一个类下的另一个方法引用,如下
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
1.4 组合
将一个类的对象封装到另一个类的对象的属性中,就叫组合。
class Gamerole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp
def equip_weapon(self, wea):
self.wea = wea # 组合:给一个对象封装一个属性改属性是另一个类的对象
class Weapon:
def __init__(self, name, ad):
self.name = name
self.ad = ad
def weapon_attack(self, p1, p2):
p2.hp = p2.hp - self.ad - p1.ad
print('%s 利用 %s 攻击了%s,%s还剩%s血'
% (p1.name, self.name, p2.name, p2.name, p2.hp))
类的私有属性
当希望有一部分的类属性(方法)只能在类的内部使用,不能在类的外部以及派生类中使用时,可以在定义这些类属性(方法)时加上__,将他们划分为类的私有属性(方法),如下
class Animal():
def __init__(self, name):
self.name = name
__p='bb'
tiger = Animal('t1')
print(tiger.__p)
##AttributeError: 'Animal' object has no attribute '__p'
二、类的继承
继承
是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。
继承使得子类能获得父类的属性(即父类中出现的变量和函数方法)
2.1单继承
创建一个类,只有继承一个父类,举例如下
class A:
def __init__(self,n):
self.n=n
def pri(self):
print(self.n)
class B(A):
pass
b=B(4) ##将b传给self,B类中没有__init__初始方法,但因为B类继承A类,就去A类中寻找初始化方法。b.n=4
b.pri() ##B类中没有pri方法,只能继续在其父类A中寻找。
2.2派生
子类也可以添加自己新的属性或重新定义这些属性(不会影响到父类),一旦重新定义了自己的属性且与父类重名,那么调用属性时,就以自己为准了。
class A:
def __init__(self,n):
self.n=n
def pri(self):
print(self.n)
class B(A):
def pri(self):
print(self.n**2)
b=B(4) ##将b传给self,B类中没有__init__初始方法,但因为B类继承A类,就去A类中寻找初始化方法。b.n=4
b.pri() ##在B类中有自己的方法,则使用子类自己的,输出得到16
super函数
class A:
def __init__(self,n):
self.n=n
def pri(self):
print(self.n)
class B(A):
def __init__(self,n,name):
super().__init__(n) ##子类通过super方法,可接纳父类的方法为子类所使用,此处将父类的init方法作为自身init方法的一部分,
self.name=name ##子类自身再对init方法进行补充
def pri(self):
print(self.n**2,self.name)
b=B(4,"pks")
b.pri()
2.3多继承
在新式类中(python3中全部都是新式类),遵循广度优先的原则,可以通过类的内置函数__mro__获取继承顺序。
class K1():
def foo(self):
print("K1-foo")
class K2():
def foo(self):
print("K2-foo")
def bar(self):
print("K2-bar")
class J1(K1, K2):
pass
class J2(K1, K2):
def bar(self):
print("J2-bar")
class C(J1, J2):
pass
if __name__ == "__main__":
print(C.__mro__)
m = C()
m.foo()
m.bar()
从上面的例子可以总结python3的寻找顺序如下图
三、类的封装
对于面向对象的封装来说,其实就是使用构造方法(__init__)将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
四、类的装饰器
@property
Python内置的**@property装饰器**就是负责把一个方法伪装成属性,调用该方法的格式和属性调用一样,但不能直接进行修改除非再引用其他装饰器。
from math import pi
class Circle:
def __init__(self,r):
self.r=r
def per(self): ##求周长
return 2*pi*self.r
@property
def area(self): ##使用装饰器,装饰类中的方法area,求面积
return self.r**2*pi
c1=Circle(4)
print(c1.per())
print(c1.area) ##装饰过的方法,可以像属性一样被调用,不用像方法一样加()进行调用
@方法名.setter
Python内置的**@方法名.setter**,对由其装饰的方法,实现它能像变量属性一样在外部被赋值。接收的赋值传入被装饰的方法中处理。
class Person:
def __init__(self,name,high,weight):
self.name = name
self.high = high
self.weight = weight
@property
def bmi(self):
return self.weight / self.high*2
@bmi.setter
def bmi(self,n): ##装饰器的格式:被装饰的方法名.setter 装饰过的方法,可以像类属性一样能在类的外部被调用进行修改。
self.weight=n
jin = Person('金老板',1.6,90)
jin.bmi=32 ##呼应bmi.setter装饰的方法bmi 修改了类的属性weight为32
print(jin.bmi) ##输出为40
@方法名.deleter
Python内置的@方法名.deleter,对由其装饰的方法,实现能在类的外部删除值
class Person:
def __init__(self,name,high,weight):
self.name = name
self.high = high
self.weight = weight
@property
def bmi(self):
return self.weight / self.high*2
@bmi.deleter
def bmi(self): ##装饰器的格式:被装饰的方法名.deleter 装饰过的方法,可以想类属性一样能在类的外部进行删除。
del self.weight
jin = Person('金老板',1.6,90)
print(jin.bmi)
print(jin.__dict__)
del jin.bmi ##呼应bmi.deleter装饰过的方法 删除对象的weight属性
print(jin.__dict__)
@classmethod
Python内置的@classmethod
,包装某个函数为类方法,该函数的第一个参数必须是cls,装饰过的方法在类的外部可以使用类名.方法完成调用
class Goods:
__discount=0.8
def __init__(self,name,price):
self.name=name
self.__price=price
@property
def price(self):
return self.__price*Goods.__discount
@classmethod ##用于装饰某个方法,装饰过的方法在类的外部可以直接使用类名.方法进行调用,无需通过指定对象才能调用.
def change_discount(cls,new_discount): ##类方法 有一个默认参数 cls 代表这个类 cls
cls.__discount=new_discount
apple=Goods('苹果',5)
print(apple.price) #输出4.0
Goods.change_discount(0.5) ##类名.方法 对应类中classmethod装饰过的方法,修改类的私有静态属性discount
print(apple.price) #输出2.5
适用场景: 想要操作类的属性(比如修改,查询)时,如果从类的某个实例进行操作,不符合逻辑.这种情况下就可以使用类方法@classmethod
@staticmethod
Python内置的@staticmethod
,将一个既和对象没有关系,也和类没有关系的函数装饰为静态方法,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。该函数的定义时括号内无需设置self或cls。可以在类的外部通过类名.函数名进行调用
class Login:
def __init__(self,name,password):
self.name = name
self.pwd = password
def login(self):pass
@staticmethod
def get_usr_pwd(): # 静态方法
usr = input('用户名 :')
pwd = input('密码 :')
Login(usr,pwd)
Login.get_usr_pwd()
四、类的反射
反射:是指通过字符串的形式,实现对象方法或属性的调用。记住在python中一切皆对象,所以python中反射应用很广
1、对对象的反射
class A:
age=29
def hahaha(self):
print('A')
a=A()
hasattr(a,"age")
print(a.age) ##对象a的age属性
print(getattr(a,"age")) ##对象a的age属性
a.hahaha() ##对象a的hahaha方法
func=getattr(a,"hahaha")
func() ##对象a的hahaha方法
2、对类的反射
class A:
age=29
def hahaha(self):
print('A')
print(A.age) ##29 类A的age属性
print(getattr(A,"age")) ##29 类A的age属性
a=getattr(A,"hahaha") ##类A的hahaha方法
a("2") ##打印A
3、对本文件的反射
import sys
def s1():
print('s1')
this_module = sys.modules[__name__] #使用__name__保证this_module始终指向本文件,无论是自主运行还是被其他程序调用。
if hasattr(this_module, 's1'):
getattr(this_module, 's1')()
4、对其他模块的反射
import test2
test2.test2()
a=getattr(test2,"test2")
a()
反射的实际用例
没学反射之前的解决方式
class User:
def login(self):
print('欢迎来到登录页面')
def register(self):
print('欢迎来到注册页面')
def save(self):
print('欢迎来到存储页面')
while 1:
choose = input('>>>').strip()
if choose == 'login':
obj = User()
obj.login()
elif choose == 'register':
obj = User()
obj.register()
elif choose == 'save':
obj = User()
obj.save()
学反射之后的解决方式
class User:
def login(self):
print('欢迎来到登录页面')
def register(self):
print('欢迎来到注册页面')
def save(self):
print('欢迎来到存储页面')
user = User()
while 1:
choose = input('>>>').strip()
if hasattr(user,choose):
func = getattr(user,choose)
func()
else:
print('输入错误。。。。')