一文彻底搞懂python面向对象

目录:(内容较多,建议收藏再观看!)

面向对象基本概念

在这里插入图片描述

类和对象

类和对象是面向对象骗程的两个核心概念。

类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用.

  • 特征其实就是一个变量,在类里我们称之为属性。
  • 行为其实就是一个函数,在类里我们称之为方法。
  • 类其实就是由属性和方法组成的一个抽象概念。

类就相当于制造飞机时的图纸,是一个模板。这个模板只规定了飞机的某些特征(例如大小,颜色,型号等等)和行为(例如起飞,降落,飞行等等),它并不是一个具体的飞机,而是对飞机的一个抽象概念。它出现的目的,是为了让我们的创建飞机对象。

在程序开发中,要设计一个类,通常需要满足一下三个要素:

  • 1.类名:这类事物的名字,按照大驼峰命名法(每个单词的首字母大写)起名。
  • 2.属性:这类事物具有什么样的特征。
  • 3.方法:这类事物具有什么样的行为。

代码举例:

class Student(object):
    #在__init__方法里,以参数的形式定义特征,我们称之为属性
    def __init__(self,name,age,height):
        self.name=name
        self.age=age
        self.height=height
    # 行为定义为一个个函数
    def run(self):
        print('正在跑步')
    def eat(self):
        print('正在吃东西')
s1=Student('小明',18,1.75)
s2=Student('小美丽',17,1.65)
print(s1.name)
s1.run()
print(s2.name)
s2.eat()

结果:

小明
正在跑步
小美丽
正在吃东西

self详解:

哪个对象调用了方法,方法里的self 指的就是谁。通过self.属性名可以访问到这个对象的属性;通过self.方法名()可以调用这个对象的方法。
在这里插入图片描述

代码举例:

class Student(object):#这里的object表示Student继承自object类
    def __init__(self,x,y):
        self.name=x
        self.age=y
    def say_hello(self):
        print('大家好,我是',self.name)
#Student('张三',18)这段代码具体做了什么呢?
#1.调用__new__方法,用来申请内存空间
#2.调用__init__方法传入参数,将self指向创建好的内存空间,填充数据
#3.变量s1也指向创建好的内存空间
s1=Student('张三',18)
s1.say_hello()
s2=Student('李四',18)
s2.say_hello()

结果:

大家好,我是 张三
大家好,我是 李四

__slots__属性的使用

限制类中的属性。

代码举例(例子说明问题):

class Student(object):#这里的object表示Student继承自object类
    def __init__(self,x,y):
        self.name=x
        self.age=y
    def say_hello(self):
        print('大家好,我是',self.name)
s1=Student('小明',18)
#python可以动态添加属性
#当使用这种方式时
#如果类里没有这种属性则添加,若有此属性则修改此对象的属性值
s1.addr='上海'#类里面原本没有定义addr属性
print(s1.addr)

结果:

上海
#如果我们想限制类里面的属性
#则可以使用__slots__属性
class Student(object):#这里的object表示Student继承自object类
    #这个属性直接定义在类里,是一个元组,用来规定对象可以存在的属性
    __slots__=('name','age')
    def __init__(self,x,y):
        self.name=x
        self.age=y
    def say_hello(self):
        print('大家好,我是',self.name)
s1=Student('张三',18)
#若此时还添加addr属性(__slots__属性值里面并不存在),就会报错
s1.addt='上海'

结果:

AttributeError                            Traceback (most recent call last)
<ipython-input-9-59af88021a4b> in <module>
     11 s1=Student('张三',18)
     12 #若此时还添加addr属性(__slots__属性值里面并不存在),就会报错
---> 13 s1.addt='上海'

AttributeError: 'Student' object has no attribute 'addt'

魔法方法:

类里的一些特殊方法

在这里插入图片描述

__init__和__del__方法:

__init__创建对象时会自动调用
__del__当对象被销毁时,会自动调用这个方法

代码举例:

class Person(object):
    def __init__(self,name,age):
        #在创建对象时,会自动调用这个方法
        print('__init__方法被调用了')
        self.name=name
        self.age=age
    def __del__(self):
        #当对象被销毁时会自动调用这个方法
        print('__del__方法被调用了')
p=Person('张三',18)
del p

结果:

__init__方法被调用了
__del__方法被调用了

repr__和__str

当打印一个对象时,会调用这个对象的__repr__或__str__方法,如果两个方法都写了则调用__str__方法
调用repr()方法,会调用对象的__repr__方法

代码举例:

class Person(object):
    def __init__(self,name,age):
        #在创建对象时,会自动调用这个方法
        print('__init__方法被调用了')
        self.name=name
        self.age=age
    def __del__(self):
        #当对象被销毁时会自动调用这个方法
        print('__del__方法被调用了')
    def __repr__(self):
        return 'repr'+self.name+' '+self.age
    def __str__(self):
        return 'str'+self.name+' '+self.age
p=Person('张三','18')
print(p)
print(repr(p))
print(p.__repr__)

结果:

__init__方法被调用了
__del__方法被调用了
str张三 18
repr张三 18
<bound method Person.__repr__ of repr张三 18>

__call__方法

可以让对象像方法一样被调用

代码举例:

class Person(object):
    def __init__(self,name,age):
        #在创建对象时,会自动调用这个方法
        self.name=name
        self.age=age
    def __call__(self,*args,**kwargs):
        print('args={},kwargs={}'.format(args,kwargs))
        fn=kwargs['fn']
        return fn(args[0],args[1])
p=Person('张三',18)
n=p(1,2,fn=lambda x,y:x+y)
print(n)

结果:

args=(1, 2),kwargs={'fn': <function <lambda> at 0x000002324313BB80>}
3

运算符的相关魔法方法:

__eq__方法

==会调用对象的_eq_方法,获取这个方法的比较结果
__eq___如果不重写,默认比较依然是内存地址

代码举例:

class Person(object):
    def __init__(self,name,age):
        #在创建对象时,会自动调用这个方法
        self.name=name
        self.age=age
    def __eq__(self,other):
        return self.name==other.name and self.age==other.age
p1=Person('张三',18)
p2=Person('张三',18)
# p1和p2是同一个对象吗?
#怎样比较两个对象是否是同一个对象?比较的是内存地址
# is身份运算符可以用来判断两个对象是否是同一个对象
print(p1 is p2)# False
#is 比较两个对象的内存地址
# ==会调用对象的_eq_方法,获取这个方法的比较结果
nums1 = [1,2,3]
nums2 = [1,2,3]
print(nums1 is nums2)# False
print(nums1 == nums2)#True
#p1==p2本质是p1.eq(p2)获取这个方法的返回值
print(p1==p2)#true,因为在Person类中已经写了__eq__方法

结果:

False
False
True
True

__ne__方法

!=本质是调用__ne__方法或者__eq__方法取反

__gt__方法

> 本质调用_ne__方法

__ge__方法

> =本质调用__ge__方法

__add__方法

+默认调用此方法

__sub__方法

-默认调用此方法

__str__方法

将对象转换为字符串时会调用此方法
打印对象时也会调用,默认是类型+内存地址

内置属性

代码说明:

class Person(object):
    '''
    hello Person
    '''
    def __init__(self,name,age):
        #在创建对象时,会自动调用这个方法
        self.name=name
        self.age=age
p=Person('张三',18)
print(p.__class__)
print(p.__dict__)#把对象和值换成为一个字典
print(p.__dir__())#等价于dir(p)
print(p.__doc__)#显示类的相关说明信息
print(p.__module__)

结果:

<class '__main__.Person'>
{'name': '张三', 'age': 18}
['name', 'age', '__module__', '__doc__', '__init__', '__dict__', '__weakref__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

    hello Person
    
__main__

对象实例的字典操作(把对象当作字典使用):

需要在类中定义定义__setitem__和__getitem__方法

代码举例:

class Person(object):
    def __init__(self,name,age):
        #在创建对象时,会自动调用这个方法
        self.name=name
        self.age=age
    def __setitem__(self,key,value):
        self.__dict__[key]=value
    def __getitem__(self,item):
        return self.__dict__[item]
p=Person('张三',18)
print(p.__dict__)#将对象转换成字典'name': '张三', 'age': 18}

#不能直接把一个对象当作字典使用
#需要定义__setitem__和__getitem__方法
p['age']=20
p['name']='tony'
print(p.name,p.age)
print(p['name'])

结果:

{'name': '张三', 'age': 18}
tony 20
tony

类属性和对象属性

简单理解:类属性是整个类共有的属性,对象属性是每个对象实例的属性,类属性的值每个对象都一样,而对象属性的值每个对象各不相同。

代码举例:

class Person(object):
    type='人类'
    def __init__(self,name,age):
        #在创建对象时,会自动调用这个方法
        self.name=name
        self.age=age
#每个实例之间的属性没有关联,互不影响
p1=Person('张三',18)
p2=Person('李四',18)
#可以通过实例对象来获取类属性
print(p1.type)
print(p2.type)

p1.type='human'
print(p1.type) #并不会修改类属性,而是给实例对象增加了一个属性

#类属性只能通过类对象来修改,实例对象无法修改类属性
Person.type='monkey'# 修改了类属性

print(p2.type)
print(Person.type)

结果:

人类
人类
human
monkey
monkey

简单图示如下:

在这里插入图片描述

私有属性和方法

不能够通过对象.属性(方法)的形式直接调用,会报错
以两个下划线开始的变量或方法为私有的

获取私有属性的方法:

  • 1.使用 对象._类名__私有变量名获取(也适用于私有方法)
  • 2.定义get和set方法来获取
  • 3.使用property来获取

私有方法可以在类中的其他方法中调用:

 def __demo(self):#__开头的方法为私有方法,在外部不能直接调用
        print('我是私有方法')
 def test(self):
        self.__demo()

代码举例:

class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
        self.__money=1000 #以两个下划线开始的变量或方法为私有的
    def get_money(self):
        return self.__money
    def set_money(self,money):
        if type(money)!=int:
            print('设置的余额不合法')
            return
        self.__money=money
p=Person('张三',18)
#获取私有变量的方法
#1.使用 对象._类名__私有变量名获取
print(p._Person__money)
#2.定义get和set方法来获取
print(p.get_money())
p.set_money(1500)
print(p.get_money())

结果:

1000
1000
1500

静态方法和类方法:

静态方法和类方法均可通过实例和类调用

代码举例:

class Person(object):
    type='rich'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    #如果一个方法里没有用到对象属性和类属性则可定义为静态方法
    @staticmethod
    def demo():
        print('hello')
    #类方法有一个参数cls,无需手动传参,指的是类对象,当前类中 cls is Person
    @classmethod
    def test(cls):#
        print(cls.type)
p=Person('张三',18)
#静态方法和类方法均可以通过实例对象和类对象调用
p.demo()
Person.demo()
p.test()
Person.test()

结果:

hello
hello
rich
rich

简单图示如下:

在这里插入图片描述

单例模式设计(了解)

简单理解为一个类从始至终只有一个实例对象

代码举例:

class Singleton:
    __instance=None
    __is_first=True
    
    @classmethod
    def __new__(cls,*args,**kwargs):
        if cls.__instance is None:
            cls.__instance=object.__new__(cls)
        return cls.__instance
    def __init__(self,a,b):
        if self.__is_first:
            self.a=a
            self.b=b
            self.__is_first=False
s1=Singleton('哈哈','嘿嘿嘿')
s2=Singleton('呵呵','嘻嘻嘻')
print(s1.a)
print(s2.a)

结果:

哈哈
哈哈

简单图例

在这里插入图片描述

继承(python中允许多继承)

在OOP(Object Oriented Programming)程序设计中,当我们定义一个class的时候,可以从某个现有的class 继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

继承有什么好处?

最大的好处是子类获得了父类的全部属性及功能(除了私有的)

代码举例:

class Animal(object):#Animal继承自object类
    def __init__(self,name,age):
        self.name=name
    def sleep(self):
        print(self.name+'正在睡觉')
class Dog(Animal):#Dog类继承自Animal类
    def bark(self):
        print(self.name+'正在叫')
#Dog()调用__new__方法,再掉用__init__方法
#Dog里没有__new__方法,会查看父类是否重写了__new__方法
#父类里也没有重写__new__方法,查找父类的父类,找到了object
#调用 __init方法,Dog类没有实现,会自动找Animal父类
d=Dog('哈士奇',3)
print(d.name)#父类里定义的属性,子类可以直接使用
d.sleep()#父类的方法子类实例可以直接调用
d.bark()

结果:

哈士奇
哈士奇正在睡觉
哈士奇正在叫

多继承举例:

若一个子类继承了多个父类
例:class A(B,C),即A类同时继承了B、C两个父类,则调用子类的某个方法时,先在A类里面找,若A类没有则会去B类方法里面找,若B类里面也没有,继续找B的父类,若B的祖先类里都没有则回去C里面找,C里面没有去C的父类里面找…(广度搜索下深度搜索)

可以用类的__mro__属性来查看查找的顺序

代码举例:

class Animal(object):#Animal继承自object类
    def __init__(self,name,age):
        self.name=name
    def sleep(self):
        print(self.name+'正在睡觉')
class Dog(Animal,object):#Dog类继承自Animal类
    def bark(self):
        print(self.name+'正在叫')
d=Dog('小哈',3)
d.bark()
print(Dog.__mro__)

结果:

小哈正在叫
(<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)

父类中的私有方法和私有属性

父类中的私有方法和属性不能继承
可以通过对象名._父类名__私有方法(或属性名进行调用)

代码举例:

class Animal(object):#Animal继承自object类
    def __init__(self,name,age):
        self.name=name
        self.__age=age
    def __sleep(self):
        print(self.name+'正在睡觉')
class Dog(Animal):#Dog类继承自Animal类
    def bark(self):
        print(self.name+'正在叫')
d=Dog('小哈 ',3)
print(d._Animal__age)
d._Animal__sleep()

结果:

3
小哈 正在睡觉

经典类与新式类(了解)

新式类和经典类的概念:

  • 新式类:继承自object的类我们称之为新式类
  • 经典类:不继承自 object的类
    在python2里,如果不手动的指定一个类的父类是object,这个类就是一个经典类
    python3中不存在经典类,都是新式类

is、isinstance和issubclass

  • is运算符用来判断是否是同一个对象
  • isinstance来判断一个对象是否由指定类(或者父类)实例化来的
  • issubclass来判断一个类是否是另一个类的子类

代码举例:

class Animal(object):#Animal继承自object类
    def __init__(self,name,age):
        self.name=name
    def sleep(self):
        print(self.name+'正在睡觉')
class Dog(Animal):#Dog类继承自Animal类
    def bark(self):
        print(self.name+'正在叫')
d1=Dog('小哈',3)
d2=Dog('小撒',3)
print(d1 is d2)
print(type(d1)==Dog)
print(isinstance(d1,Dog))
print(isinstance(d1,Animal))
print(issubclass(Dog,Animal))
print(issubclass(Animal,Dog))

结果:

False
True
True
True
True
False

多态

多态是基于继承,通过子类重写父类的方法,达到不同的子类对象调用相同的父类方法,得到不同的结果以提高代码的灵活度

方法重写(子类重写父类方法)

重写方法场景:(重写的是同名方法)

  • 子类的实现和父类的实现完全不一样。
  • 子类在父类的基础上又有更多的实现。

对于情况1的代码举例:

class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def work(self):
        print(self.name,'正在工作')
class Student(Person):
    #与父类方法完全不同的重写
    def work(self):
        print(f'我叫{self.name},今年{self.age}岁,我正在工作')
s=Student('张三',18)
s.work()

结果:

我叫张三,今年18岁,我正在工作

对于情况2的代码举例:

class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def work(self):
        print(self.name,'正在工作')
class Student(Person):
    #子类在父类的基础上又增加了新功能,需要重写父类方法
    def __init__(self,name,age,addr):
        super(Student,self).__init__(name,age)#super括号里面的内容可以省略
        self.addr=addr
s=Student('张三',18,'上海')
s.work()

结果:

张三 正在工作

多态的简单使用代码举例

class Dog(object):
    def work(self):
        print('狗正在工作')
class PoliceDog(Dog):
    def work(self):#子类重写父类方法
        print('警犬正在执行任务')
class BlindDog(Dog):
    def work(self):
        print('导盲犬正在引路')
class DrugDog(Dog):
    def work(self):
        print('缉毒犬正在缉毒')

class Person(object):
    def __init__(self,name):
        self.name=name
        self.dog=None
    def work_with_dog(self):
        if self.dog is not None and isinstance(self.dog,Dog):
            self.dog.work()
p=Person('张三')

pd=PoliceDog()
p.dog=pd
p.work_with_dog()

bd=BlindDog()
p.dog=bd
p.work_with_dog()

dd=DrugDog()
p.dog=dd
p.work_with_dog()

结果:

警犬正在执行任务
导盲犬正在引路
缉毒犬正在缉毒

结束,谢谢观看!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值