九、类和实例

一、类的基础概念

是一个抽象的概念,可以理解为具有相同属性的一组对象的集合,类的属性有两种

  • 属性(类定义中的变量,别名:静态变量,静态属性)
  • 方法(类定义中的函数,别名:动态变量,动态属性)

实例则是类的一个具体的对象,实例唯一操作就是属性引用。

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(指实例本身),在创建实例时就会调用初始化方法,传递参数必须与初始化方法对应。

实例化一个对象总共发生了三件事:

  1. 在内存中开辟了一个对象空间。
  2. 自动执行类中的__init__方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
  3. 在__init__ 方法中通过self给对象空间添加属性。

类名称空间和对象空间的关系
在这里插入图片描述
从图中可以知道对象之所以能找到类中的属性,是因为有个类对象指针指向类的名称空间.

扫描二维码关注公众号,回复: 9118668 查看本文章

对象查找属性的顺序:先从对象空间找 ------> 类空间找 ------> 父类空间找 ------->…

类名查找属性的顺序:先从本类空间找 -------> 父类空间找--------> …

上面的顺序都是单向不可逆,类名不可能找到对象的属性。

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('输入错误。。。。')
发布了40 篇原创文章 · 获赞 2 · 访问量 2062

猜你喜欢

转载自blog.csdn.net/weixin_42155272/article/details/93999645
今日推荐