Python基础之面向对象基础

一:面向对象

面向对象:OOP,随着计算机需要解决问题的规模扩大,情况越来越复杂,需要很多人,很多部门协作,面向过程编程不太适合了。
什么是面向对象呢:一种认识世界,分析世界的方法论。将万事万物抽象为类。
类Class:
类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合。用计算机语言来描述类,就是属性和方法的集合。
对象 Instance、object:对象是类的具象,是一个实体。对于我们每个人这个个体,都是抽象概念人类的不同的实体。
Python中一切皆对象。对象是数据和操作的封装;对象是独立的,但对象之间可以相互作用;目前OOP是最接近人类认知的编程范式。

面向对象的三要素:
封装:
组装:将数据和操作组装到一起。
隐藏数据:对外只暴露一些接口,通过接口访问对象。比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要知道使用什么部件怎么驾驶,可以不了解后面的机动原理
继承:
多复用,继承来的就不用自己写了
多继承少修改,OCP ( open-closed principle ),使用继承来改变,来体现个性
多态:
面向对象编程最灵活的地方,动态绑定

二:Python的类

2.1 定义类

class MyClass:
    '''
    必须使用class关键字
    类名必须是用大驼峰命名
    类定义完成后,就产生了一个类对象,绑定到了ClassName上
    '''
    x = 'abc'           # 类属性
    def foo(self):      # 类属性foo,也是方法
        return self.x
print(MyClass.x)
print(MyClass.foo)
print(MyClass.__doc__)

2.2 实例化

class MyClass:
    x = 'abc'
    def foo(self):			# self必须为第一个参数;指代实例本身
        return self.x
obj = MyClass()				# 实例化,初始化
print(obj.x)
print(obj.foo())

实例化过程:

  1. 创建了一个对象空间,实例空间
  2. 自动执行__init__方法,并将对象空间传递给self
  3. 执行具体的__init__方法,给对象空间封装属性

2.3 __init__方法

class MyClass:
    x = 123
    def __init__(self):			# 初始化
        print('init method')
    def foo(self):
        return self.x
obj = MyClass()					# 会调用__init__方法
print(obj.foo())

MyClass()实际上调用的是__init__(self)方法,可以不定义,如果没有定义会在实例化后隐式调用。
它的作用就是对实例进行初始化。
__init__方法可以有多个参数:

class Person:
    x = 'abc'
    def __init__(self, name, age):
        self.name = name
        self.age = age
a = Person('tom', 20)
b = Person('Jerry', 18)
print(a.name, b.name)
print(a.x, b.x)

注意:__init__方法不能有返回值,也就是只能是None

2.4 self

class Person:
    def __init__(self):
        print('self in init method = {}'.format(id(self)))
tom = Person()
print('tom = {}'.format(id(tom)))

打印结果:

self in init method = 2152217142152
tom = 2152217142152

上例说明:self就是调用者,就是tom对应的实例对象

2.5 类的特殊属性

实例变量是每一个实例自己的变量,是自己独有的;类变量是类的变量,是类的所有实例共享的属性和方法

__name__				# 对象名
__class__				# 对象的类型
__dict__				# 对象的属性的字典
__qualname__			# 类的限定名
class Person:
    x = 123
    def __init__(self, name):
        self.name = name
    def show(self):
        return self.x
a = Person('tom')
b = Person('jerry')

print(a.__class__, b.__class__)
# <class '__main__.Person'> <class '__main__.Person'>
print(a.__class__.__qualname__)     # __qualname__类上才有这个属性
# Person
print(a.__class__.__name__)
# Person
print(isinstance(a, a.__class__))
# True
print(Person.__dict__)
# {'__module__': '__main__', 'x': 123, '__init__': <function Person.__init__ at 0x00000221142FBA68>, 'show': <function Person.show at 0x000002211622BA68>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(a.__dict__)
# {'name': 'tom'}
print(b.__dict__)
# {'name': 'jerry'}

实例属性的查找顺序:
指的是实例使用点来访问属性,会先找到自己的__dict__,如果没有,然后通过属性__class__找到自己的类,再去类的__dict__中找
注意:如果实例使用__dict__[变量名]访问变量,将不会按照上面的查找顺序找变量了。
一般来说,类变量使用全大写来命名。

2.6 类方法与静态方法

class MyClass:
    a = 'XXX'
    def foo(self):
        print('foo')
    @classmethod			# 类方法
    def clsmtd(cls):
        print('{}.a={}'.format(cls.__name__, cls.a))
    @staticmethod			# 静态方法
    def staticmtd(x):
        print('static')

MyClass.clsmtd()
b.clsmtd()          		# 通过b.__class__.clsmtd()调用
MyClass.staticmtd('abc')
b.staticmtd(4)        

2.7 私有属性

私有属性:使用双下划线开头的属性名,就是私有属性

class Person:
    def __init__(self, name, age = 18):
        self.name = name
        self.__age = age
    def grow_up(self, incr = 1):
        if 0 < incr < 150:
            self.__age += incr
    def get_age(self):
        return self.__age
tom = Person('tom')
tom.grow_up(2)
# print(tom.__age)				# 访问报错
print(tom.get_age())			# 可以通过方法来访问私有属性

通过上面的实例可以看出,外部已经访问不到__age了,而age又根本就没有定义,更是访问不到。
那么如何访问这个私有变量__age呢?可以在内部定义一个方法来返回私有属性。
注意:Python中的私有属性其实并不私有,我们可以输出下__dict__来看下:

print(Person.__dict__)					# 没有私有属性
print(tom.__dict__)
# {'name': 'tom', '_Person__age': 20}	# 变量名内部被修改为:_Person__age了
# 也就是可以通过 tom._Person__age 来访问,修改该私有属性

私有变量能不能外部被定义?

class A:
    __country = 'China'		# 导致该私有变量在类的内部会发生变形
print(A.__dict__)
A.__Language = 'Chinese'
print(A.__dict__)

输出结果:

{'__module__': '__main__', '_A__country': 'China', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
{'__module__': '__main__', '_A__country': 'China', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, '__Language': 'Chinese'}

私有变量是否可以被继承

class A:
    __country = 'China'
    def __init__(self, name):
        self.__name = name
class B(A):
    def get_name(self):
        return self.__name
b = B('andy')
print(b.__dict__)		# 存在私有变量
print(b.get_name())		# 但这里无法获取到私有变量

2.8 保护变量

在变量名前使用一个下划线,称为保护变量

class Person:
    def __init__(self, name, age = 18):
        self._name = name
        self.__age = age
    def grow_up(self, incr = 1):
        if 0 < incr < 150:
            self.__age += incr
    def get_age(self):
        return self.__age
tom = Person('tom')
tom._name = 'Jerry'
print(tom.__dict__)			# {'_name': 'Jerry', '_Person__age': 18}
print(tom._name)			# Jerry

_name变量在__dict__中没有被修改名称,和普通的属性一样,可以外部直接访问,修改该属性
Python解释器只认双下划线的属性,单下划线的属性不做任何特殊处理。这只是开发者间的共同约定,看见这种变量就如同私有变量一样,不要直接使用。

2.9 私有方法

class Person:
    def __init__(self, name, age = 18):
        self._name = name
        self.__age = age
    def __grow_up(self, incr = 1):
        if 0 < incr < 150:
            self.__age += incr
    def get_age(self):
        return self.__age
tom = Person('tom')
print(Person.__dict__)
# {'__module__': '__main__', '__init__': <function Person.__init__ at 0x0000029C4757BA68>, '_Person__grow_up': <function Person.__grow_up at 0x0000029C4757BD38>,........................

如同私有变量一样,私有方法名在Python内部被改名了,_Person__grow_up

2.10 属性装饰器

一般好的设计是:把实例的属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性

class Person:
    def __init__(self, chinese, english, history):
        self.chinese = chinese
        self._english = english
        self.__history = history
    def get_history(self):
        return self.__history
    def set_history(self, value):
        self.__history = value
    @property
    def history(self):				# 类似上面的get_history方法,只是它是一个属性方法
        return self.__history
    @history.setter					# 类似上面的set_history
    def history(self, value):
        self.__history = value
    @history.deleter
    def history(self):
        # del self.__history
        print('delete history')
stu = Person(80,88,90)
stu.history = 99
print(stu.history)
del stu.history

property装饰器:后面跟的函数名就是以后的属性名。它就是getter,这个必须有,有了它至少是只读属性
setter装饰器:与属性名同名,且接收2个参数,第一个self,第二个就是将要赋值的值。有了它,属性可写
deleter装饰器:可以控制是否删除属性。很少用。
property装饰器必须在前,setter,deleter装饰器在后。它能通过简单的方式把方法的操作变成对属性的访问,并起到一定的隐藏效果。
除了上面的装饰器的写法,还有另一种写法:

class Person:
    def __init__(self, chinese, english, history):
        self.chinese = chinese
        self.__english = english
        self.__history = history
    def get_english(self):
        return self.__english
    def set_english(self, value):
        self.__english = value
    # english = property(get_english, set_english)		# get_english方法也可以用lambda表达式来代替
    english = property(lambda self:self.__english, set_english)

stu = Person(50, 60, 70)
stu.set_english(90)
print(stu.get_english())
print('=' * 50)
stu.english = 99
print(stu.english)

上例中的property方法源码如下:
Python面向对象

2.11 对象的销毁

类中可以定义__del__方法,称为析构函数(方法)
作用:销毁类的实例的时候调用,以释放占用的资源
由于Python实现了垃圾回收机制,这个方法不能确定何时执行。有必要时,可以使用del语句删除实例来手动调用该方法。

class Person:
    def __init__(self, chinese, english, history):
        self.chinese = chinese
        self.__english = english
        self.__history = history
    def __del__(self):
        print('delete {}'.format(self.chinese))
stu = Person(50, 60, 70)
print(stu.chinese)

2.12 方法重载(overload)

在其他面向对象的高级语言中,都在重载的概念。
所谓重载就是同一个方法名,但是参数数量,类型不一样,就是同一个方法的重载。
Python没有重载。Python不需要重载。
Python中方法(函数)定义形参非常灵活,不需要指定类型(就算指定了也只是一个说明而非约束),参数个数也不固定(可变参数)。一个函数的定义可以实现很多种不同形式实参的调用。所以Python不需要方法的重载。

三:类的继承

查看继承的特殊属性和方法有:

特殊属性和方法含义示例
__ base __类的基类
__ bases __类的基类元组
__ mro __显示方法查找顺序,基类的元组
mro()方法同上int.mro()
__ subclasses __()类的子类列表int.__ subclasses__()

3.1 单继承

继承的概念:子类拥有父类的所有方法和属性

class Animal:
    def eat(self):
        print('eat')
    def drink(self):
        print('drink')
class Dog(Animal):
    pass
wangcai = Dog()
wangcai.eat()
wangcai.drink()

3.2 继承的传递性

C类从B类继承,B类又从A类继承,那么C类就具有B类和A类的所有属性和方法

class Animal:
    def eat(self):
        print('eat')
    def drink(self):
        print('drink')
class Dog(Animal):
    def bark(self):
        print('bark')
class XiaoTianQuan(Dog):
    pass
x = XiaoTianQuan()
x.eat()
x.bark()

3.3 方法的重写

当父类的方法实现不能满足子类需求时,可以对方法进行重写

class Animal:
    def eat(self):
        print('eat')
    def drink(self):
        print('drink')
class Dog(Animal):
    def bark(self):
        print('bark')
class XiaoTianQuan(Dog):
    def bark(self):			# 在子类中定义一个和父类同名的方法即可重写
        print('xiaotianquan bark')
x = XiaoTianQuan()
x.bark()

对父类方法进行扩展

class Animal:
    def eat(self):
        print('eat')
    def drink(self):
        print('drink')
class Dog(Animal):
    def bark(self):
        print('bark')
class XiaoTianQuan(Dog):
    def bark(self):
        super().bark()      # 调用父类的bark()方法
        print('xiaotianquan bark')
x = XiaoTianQuan()
x.bark()

super是一个特殊的类,super()就是使用super类创建出来的对象

3.4 多继承

子类可以拥有多个父类,并且具有所有父类的属性和方法

class A:
    def test(self):
        print('test method')
class B:
    def demo(self):
        print('demo method')
class C(A, B):
    pass
x = C()
x.test()
x.demo()

3.5 MRO

Python中针对类提供一个内置属性__ mro__,可以查看方法搜索顺序
MRO是method resolution order,主要用于在多继承时判断方法,属性的调用路径

class A:
    def test(self):
        print('test method')
class B:
    def demo(self):
        print('demo method')
class C(A, B):
    pass
x = C()
# print(x.__class__.__mro__)		# 或者使用类来调用__mro__方法
print(C.__mro__)
# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

四:多态

多态:不同的子类对象调用相同的父类方法,产生不同的执行结果
多态可以增加代码的灵活度;以继承和重写父类方法为前提;是调用方法的技巧,不会影响到类的内部设计

class Dog(object):
    def __init__(self, name):
        self.name = name
    def game(self):
        print('{} is playing'.format(self.name))

class XDog(Dog):
    def game(self):
        print('{} flying to the sky'.format(self.name))

class Person(object):
    def __init__(self, name):
        self.name = name
    def game_with_dog(self, dog):
        print('{} and {} is playing'.format(self.name, dog.name))
        dog.game()

w = Dog('WangCai')
j = Dog('jerry')
x = Person('Jet')

x.game_with_dog(w)
x.game_with_dog(j)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值