python笔记-面向对象

类的定义

class 关键字

语法

class 类名():
'''
类的描述
'''
    
class Animals():
    name='cat'
    def running(self):
        n=self.name
        print(f'{n} can run !')

类的方法

静态方法

定义时,在方法名的上一行使用 @staticmethod装饰器
不传入self
不需要类进行实例化就能使用

应用

一般把与类无关或者与实例对象无关的方法定义为静态方法,可以用于类入口的检测功能,比如在类实例化之前检测当前条件是否需要执行类实例化操作等

定义

class Animals():
    name='nebu'
    @staticmethod
    def eat(name):
        print(f"{name} is eating!")

调用

n2="lily"
Animals.eat(n2)

输出

lily is eatting!

类方法

定义

  • 在方法名上一行添加装饰器 @classmethod
  • 第一个参数要设置为 cls ,代表自身类(尚未实例化的类)
  • 不需要类进行实例化就能直接使用
class Animals():
    n='cat'
    @classmethod
    def sleep(cls):# 类方法
        myname=cls.n
        print(f'{myname} is sleeping')

调用

Animals.sleep()

输出

cat is sleeping

应用

类方法在重新定义类的时候无须修改构造方法__init__(),将需要重构的代码以类方法的形式表示即可

class Dates():
    def __init__(self,year,month,day):
        self.day=day
        self.month=month
        self.year=year
        
    def get_date(self):
        date=f'{self.year}-{self.month}-{self.day}'
        print(date)
        
    @classmethod
    def get_second_data(cls,date_string):# 通过类方法实现新的功能,不用修改类的构造方法
        year,month,day=date_string.split(":")
        date=cls(year,month,day)
        return date
        
mydate=Dates('2022','1','20')#创建一个实例化对象
mydate.get_date() #调用实例方法

print('我是分隔符'*10)

testdate=Dates.get_second_data('2021:12:14')# 创建实例化对象并通过类方法 传入数据
testdate.get_date()# 调用实例方法输出日期

输出:

2022-1-20
我是分隔符我是分隔符我是分隔符我是分隔符我是分隔符我是分隔符我是分隔符我是分隔符我是分隔符我是分隔符
2021-12-14

实例方法

定义

  • 在定义时第一个参数要设置为self ,代表自身对象(类实例化所生成的对象)
  • 必须在类实例化之后才能使用
class Animals():
    n='cat'
    def running(self):   #实例方法
        n=self.name
        print(f'{n} can run !')

调用


#实例化一个对象
tiger=Animals()
tiger.running()  #实例化对象调用该方法

输出

cat can run !

应用

实例方法是我们常用的方法,它为类提供了功能实现的载体,类的核心功能主要由实例方法实现


类的property属性

类中重要的两个元素是属性和方法,当要对属性进行一些额外操作
两种实现方法

  1. 使用property函数,将需要操作的属性设置为一个property对象
myatt=property(fget,fset,fdel,doc)

将对这个属性定义的一系列操作(方法)如获取属性的值,修改属性值,删除属性值作为参数传入对象

class People():
    def __init__(self,name):
        self.name=name
        
    def set_name(self,name):
        self._name=name
        
    def get_name(self):
        return self._name
        
    def del_name(self):
        del self._name
        
    name = property(get_name,set_name,del_name)
a=People("lily")
a.set_name('nebula')
b=a.get_name()
print(b)
nebula
  1. 以上操作也可以通过装饰器实现。

@property

有setter,getter,deleter三个方法;
现在还无法理解 self. 后面的短横线有什么用

class People:
     
    @property
    def name(self):
        return self._name
    @name.setter    
    def set_name(self,name):
        self._name=name
   
    @name.deleter
    def del_name(self):
        del self._name
a=People()
a.set_name='nebula'
print(a.name)

实例化

定义类时,如果没有重写初始化方法 init
实例化时只用写上类名和括号

class People:
    pass
    
xiaoming=People()#实例化

如果重写了 init 方法,实例化时就要按要求传参

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age
        
a=People('nebula','13')
# print(a.name)

使用
分别调用类方法、静态方法、实例方法、带属性的方法

在业界的开发规范中,类属性、静态方法、类方法、实例方法和带property属性的方法使用说明如下:
(1)静态方法和类方法应由未实例化的类调用。
(2)类属性、实例方法和带property属性的方法应由已实例化的对象调用。

class Animals:
    name='小白'
    
    def __init__(self,name,*args,**kwargs):
        self.name=name
        
    @staticmethod
    def welcome(name):
        print(f"welcome to you,my master i'm {name}")
        
    @classmethod
    def run(cls):
        print(f'{cls.name} is running!')
        
    @property
    def eats(self):
        return f'{self.name} is eating {self.foods}'
        
    @eats.setter
    def set_eat(self,foods):
        self.foods=foods
        
    def sleep(self):
        print(f'{self.name} is sleeping')
        
a=Animals("z小花")
# Animals.welcome("小紫")调用静态方法
a.welcome('buzhi')#调用静态方法2

Animals.run() #调用类方法
a.run()#调用类方法2

# 调用实例方法
a.sleep()

#调用带属性的方法
a.set_eat="hamburger"
print(a.eats)
   
welcome to you,my master i'm buzhi
小白 is running!
小白 is running!
z小花 is sleeping
z小花 is eating hamburger  

动态添加属性

在实例化对象前动态添加属性

def run(self):
    print("this is running")
class Animals:
    pass
    
#在实例化之前动态添加属性
Animals.name='abc'

#实例化后动态添加属性
a = Animals()
a.age='14
a.run=run

#调用
print(a.name)#实例化前添加的属性

print(a.age)#调用实例化后添加的属性
a.run()#调用实例化后添加的方法

还可以通过内置函数 setattr() 来实现
语法:

setattr(obj,attname,attrvalue)

示例

#在实例化之前动态添加属性
Animals.name='abc'

#实例化后动态添加属性
a = Animals()
a.age='14
a.run=run

#上面代码可以写成

#在实例化之前动态添加属性
setattr(Animals,'name','abc')

#实例化后动态添加属性
a = Animals()
setattr(a,'age','14')
setattr(a,'run',run)

内置属性__slots__

Python在类里面设置了内置属性__slots__,它以元组或列表格式表示,只有元组或列表里的元素才能作为动态添加的属性或方法的名称

内置属性__slots__只对当前的类起作用,它对派生类(子类)不起任何作用

现在理解到的作用就是对动态添加属性和方法名,做了一些限制和规定

# 内置属性 __slots__
def jump():
    print("this is jumping")
    
def run():
    print("this is run!")
    
def laughing():
    print("this is laughing!")
    
class People:
    __slots__ =['run','laughing']   #限定了实例能添加的属性或方法名称 ,只能是run 和laughing
    
if __name__=='__main__':
    a=People()
    a.run=run
    a.run()

输出:

this is run!

如果添加没有在属性设定列表中的jump ,就会报错


if __name__=='__main__':

    a=People()
    # a.run=run
    # a.run()
    a.jump=jump
    a.jump()

输出:

AttributeError: 'People' object has no attribute 'jump'

类的封装

类的封装(Encapsulation)是指类的属性(变量)和方法封装在该类内,只有该类中的成员才可以使用和访问,在类的外部是无法使用和访问的

被封装的属性和方法,叫做该类的:

  • 私有变量(private Variable)
  • 私有方法(Private Method)

如何定义私有变量和私有方法

  • 在定义的方法或者变量名称前 加上两个下划线 '__'

class People:
    __name='nebula'
    def __run(self):
        print("this is runing")

  • 在定义的方法或变量前加一个下划线“_”
    这种方法的调用方式与公有方法一致,主要作用是开发人员对变量或方法做标记

如何调用私有变量和私有方法

调用:
a._classname__methodname
解释

正常的调用方式,会报错

a.__run()
AttributeError: 'People' object has no attribute '__run'

使用内置函数__dir__()查看对象的所有属性和方法,找到了私有属性对应的真正的名称,我们通过这个名称调用

print(a.__dir__())
['__module__', '_People__name', '_People__run', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__init__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

可以看到有格式为 _类名__方法名(属性名)的方法和属性。对应上面定义的 __name 和 __run;
我们以这种格式调用

a._People__run()
this is runing

类的继承

类的继承(Inheritance)是新类继承旧类的属性与方法,这种行为称为派生子类(Subclass)。继承的新类称为派生类(子类)(Derived Class),被继承的旧类则称为基类(父类)(Base Class)。

当基类的属性和方法不能满足开发需求的时候,我们可以在基类的基础上定义派生类,由派生类实现复杂多变的开发需求

语法:

class myclassname(class1,class2,class3,...):
    """
    myclassname : 派生类的名称
    class1... :被继承的类名称,也就是基类名称
    """
    pass

继承可分为:

  • 单一继承
    只继承自一个类
  • 多重继承
    继承两个或两个以上不同的类
class Animals():
    color='red'
    def run(self):
        return 'running'
        
class Dogs(Animals):      # 单一继承
    dogName="little black dog"
    def speak(self):
        return 'speaking'
        
class Cats(Animals):
    catName='little white cat'
    def sleep(self):
        return 'sleeping'
        
class Pets(Dogs,Cats):      #多重继承

    def run(self):
        ''' 重写基类方法 run() '''
        return 'running and jumping'
        
    def mypets(self):
        print(f"I have two pets,their color is {self.color}")
        print(f"My dog's name is {self.dogName}")
        print(f"My cat's name is {self.catName}")
        print(f"My dog can {self.speak()}")
        print(f"My cat can {self.sleep()}")
        print(f"They both can {self.run()}")
        
if __name__=='__main__':
    p=Pets()
    p.mypets()

输出:

I have two pets,their color is red
My dog's name is little black dog
My cat's name is little white cat
My dog can speaking
My cat can sleeping
They both can running and jumping

重写基类方法:派生类定义的方法名与基类的方法名一致,则是为派生类重写的基类的方法,调用时只调用重写的方法

钻石继承体系 也成为菱形继承, 出现这种继承过程,会使程序出现一些问题,所以应该避免出现

Animals 、cats、dogs、pets这几个类的继承过程,就构成了钻石继承体系

34a3f6efe77c76dff8b44cc78a1d8b46.png


内置函数: super()

内置函数super()是为了解决多继承而存在的,理解super()的原理之前,先要了解MRO规则,表示类继承体系中的成员解析顺序

使用super可以避免重复调用

派生类的方法调用基类的方法,可以通过函数 super()
实际上,super()所指的不是父类,而是MRO中的下一个类。

class Animals():
    color='red'
    def run(self):
        return 'running'
        
class Dogs(Animals):# 单一继承
    dogName="little black dog"
    def speak(self):
        re=super().run()  #通过函数super调用基类的 run()方法,还能获取基类方法的返回值
        # return 'speaking'
        return re

查看类的MRO,可以使用mro()方法

print(className.mro())

MRO (Method resolution Order)方法解析顺序

Python 类是支持(多)继承的,一个类的方法和属性可能定义在当前类,也可能定义在基类。针对这种情况,当调用类方法或类属性时,就需要对当前类以及它的基类进行搜索,以确定方法或属性的位置,而搜索的顺序就称为方法解析顺序

MRO 深度优先搜索

一个类继承了多个类,在执行方法时,优先搜索基类1,父类,父类的父类,层层往上;如果没有找到对应的方法,再去搜索基类2,基类2的父类,父类的父类。

如下:方法查找的顺序,C,A,A的父类F,B,B的父类F

class F:
    pass
class A(f):
    pass
    
class B(f):
    pass
       
class C(A,B)
    pass
    
print(C().method())

MRO 广度优先搜索

在执行类的方法时,优先搜索直接继承的基类1,基类2,然后再往上搜索基类的父类。

上方的例子,搜索顺序就为:C,A,B,F

MRO C3
python 3 唯一支持的方式

pass

类的多态

类的多态是指类可以有多个名称相同、参数类型不同的函数方法

python 没有明显的多态性

可以通过两种方式实现:

  • 设置实例方法的可变参数和关键字参数
    在一个类里面,只要在函数方法里面使用可变参数和关键字参数即可实现类的多态
class Animals:
    def doing(self,*args,**kwargs):
 
        action='' if args and kwargs else 'running'
        if args:
            action=' and '.join(list(args))
        if kwargs:
            for k,v in kwargs.items():
                action=action+str(v) if not action else action+" and "+str(v)
        print(f"Animals is {action}")
if __name__=='__main__':
    a=Animals()
    a.doing()
    a.doing('sleeping','dancing',jump='jumping',sing='singing')
  • 在不同的类之间实现
    比如派生类重写基类的某个方法或者两个不相关的类定义了相同的方法名

动态创建类

使用内置函数 type() 动态创建类
语法:

type ( classname, ( 基类name, ) , { 属性:属性值,方法:方法名  } )

基类是以元组的形式表示
属性和方法以字典形式表示

使用方法:

(1)如果要为类设置函数方法,就必须事先定义好相应的函数方法。
(2)如果是类方法、静态方法、实例方法或者带property属性的方法,它们的定义方式与Class关键字定义的时候相同。
(3)定义好的方法以key-value(字典格式)传入type(),字典的键值对只需传入方法名称即可,无须添加小括号。

#使用内置函数type()动态创建类
class Animals:
    color='purple'
    def doing(self):
        print("animals")
   
#动态创建类
if __name__=='__main__':
    def run(self,name):
        self.name=name
        print(f"{name} is running")
    def doing(self):
        print("cat doing")
    cat=type('cat',(Animals,),{'color':'red','doing':doing,"run":run})#创建类cat
    c=cat()#实例化
    c.doing()
    c.run('haha')

输出

cat doing
haha is running

创建类的类:元类(metaclass)

元类是创建类的类,类其实也是对象;

pass

工厂模式

简单的工厂模式:

(1)抽象产品角色:以基类的形式表示,将多个产品的相同属性和方法定义在一个抽象类里面。
(2)具体产品角色:以派生类的形式表示,它是继承抽象产品角色,能具体反映现实中的某个事物。
(3)工厂角色:以类或函数的形式表示,并且含有一定的业务逻辑和判断逻辑,它的功能是由具体产品角色的实例化对象执行的。

练习:

class Person:
    pass
class Male(Person):
    def __init__(self,name):
        print("Hello,Mr."+name)
class Female(Person):
    def __init__(self,name):
        print("Hello,Miss."+name)
class Factory:
    def get_person(self,name,gender):
        if gender=='M':
            return Male(name)
        elif gender == 'F':
            return Female(name)
'''在 Factory 类中定义的方法,获取用户传入的参数name和gender,根据判断gender的值,创建对应类的实例'''
if __name__=='__main__':
    factory=Factory()
    person=factory.get_person("Smith",'M')

posted @ 2021-12-27 17:48  深海鱼香茄子  阅读(24)  评论(0编辑  收藏  举报