美文网首页
Python OOP

Python OOP

作者: 李霖弢 | 来源:发表于2019-12-16 16:52 被阅读0次

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数。

面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

class Student(object):
    __slots__=("__name" , "score")
    age = 18
    def __init__(self, name, score):
        self.__name = name
        self.score = score
    def print_score(self):
        print(self.__name , self.score , Student.age)

类中的函数(包括构造函数),第一个形参都指向实例自身

成员

成员的增删改查
  • 通过dir()可以获取一个对象所有的成员的List
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
  • 通过hasattr()setattr()getattr()判断/获取/赋值指定成员
>>> hasattr(obj, 'x')
True
>>> obj.x
9
>>> getattr(obj, 'y') # 获取属性'y' 没有则报错
>>> getattr(obj, 'y', 20) # 获取属性'y' 没有则使用默认值20
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
  • 通过del可以删除实例的成员
del jack.score
私有成员

在类中定义的成员,如以__开头,则为私有成员
jack实例的Student类中的__name,在类外会被解释器变为_Student__name(不同版本解释器可能不一样)
因此外部无法访问jack.__name,却能访问jack._Student__name,如对jack.__name赋值也不会覆盖jack._Student__name
既以__开头又以__结尾的视为特殊成员,不受此影响

_开头的成员一般约定为私有成员,但解释器不会对其做任何操作

特殊成员

既以__开头又以__结尾的成员,通常不需要人为声明

  • __len__方法
    len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:
>>> len('ABC')
3
>>> 'ABC'.__len__()
3
  • __slots__
  • __init__()
  • __class__
    该对象的类型(类则为type,实例则为其类)
    可以通过self.__class__.调用类属性
  • __str__()__repr__()
    变量/对象被print()时输出的字符串
  • __iter__()__next__()
    使类可以被for...in循环
  • __getitem__()__setitem__()__delitem__()
    把对象视作listtupledict进行赋值/删除成员
class Test():
    age = 18
    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, key, value):
        setattr(self, key, value)
test = Test()
test["age"] = 88
print(test.age)  # 88
print(test["age"])  # 88
  • __getattr__()
    没有找到的属性可以交给__getattr__()进行后续处理,而不报错
    存在的属性不会进入__getattr__()逻辑
class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
s.age()#25
  • __call__()
    使实例本身可以如同一个函数被调用
class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)

s = Student('Michael')
s() # self参数不要传入 My name is Michael.

通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

类成员、实例成员、静态方法

不同于JS和C#,python当试图获取/判断实例的某个成员是否存在时,若不存在则会取得其class的同名成员,因此建议不要对实例属性和类属性使用相同的名字
该特性对hasattrgetattrdir均有效,但可以通过 <类名>.__dict__<实例>.__dict__ 可以获取类和实例的属性字典,以查看该属性是属于类还是实例

  • 两种属性

    • 类属性
      直接在类中声明
    • 实例属性
      通常在方法中通过self.进行声明
  • 三种方法,实例对象和类对象都可以调用

    • 实例方法
      用于给实例调用的方法,其实还是在类上,可以被类的__dict__获取
      第一个参数必须是实例对象(一般约定为self),通过它来传递实例的属性和方法;
    • 类方法
      使用装饰器@classmethod。第一个参数必须是当前类对象(一般约定为cls),通过它来调用类的方法或修改类的属性
    • 静态方法
      使用装饰器@staticmethod。参数随意,没有selfcls参数,但是方法体中不能使用类或实例的任何属性和方法,一般用于和类对象以及实例对象无关的代码;
class CocaCola:
    formula = ['caffeine', 'sugar', 'water', 'soda']
    def drink(self):
        print('Energy!')
    @classmethod
    def drink2(cls):
        print('Energy2!')
    @staticmethod
    def drink3():
        print('Energy3!')

coke = CocaCola()
coke.drink()
CocaCola.drink(coke)
coke.drink2()
CocaCola.drink2()
coke.drink3()
CocaCola.drink3()
限制成员内容

通过对类成员__slots__赋值一个tuple,可以限制该class能添加的实例属性
注意__slots__不会影响子类,除非在子类中也定义__slots__,此时子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

限制成员读写
  • @property 装饰器规定了成员的读取,并自动创建@XXX.setter装饰器
  • @XXX.setter会对成员写入进行必要检查
  • 当只有@property时为只读属性
class Student(object):

    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        self._birth = value

    @property
    def age(self):
        return 2015 - self._birth

继承和多态

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

  • Python3中,类默认继承object,因此写或不写都一样
  • Python2中若不写,会少获得一些方法

动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

多重继承
class Dog(Mammal, Runnable):
    pass

通常,用于给一个类增加额外功能的类,其类名以MixIn结尾,这种设计也称为MixIn。

JS(TS)、C#、Python中类继承时父类构造函数的调用情况
  • JS
    可以显式在子类的constructor中调用super() (TS中为必须调用)
    如果子类未声明constructor则会在隐式定义的constructor中调用super()执行父类构造函数
  • C#
    子类静态构造函数 => 父类静态构造函数 => 父类实例构造函数 => 子类实例构造函数
  • Python
    通常在子类的__init__函数中调用super().__init__()
    如无调用则不会触发父类构造函数

枚举类

通过Enum类实例化直接生成枚举
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

其中value属性则是自动赋给成员的int常量,默认从1开始计数。

Enum派生出自定义类自定义枚举

@unique用于检查没有重复值

from enum import Enum, unique
@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
print(Weekday.Mon== Weekday["Mon"])#True
print(Weekday.Mon== Weekday(1))#True
print(Weekday.Tue.value)#2

元类

type

type是所有类(包括自己)的类型,因此type()函数可以查看一个类型或变量的类型,也可以创建一个新类

print(type(Hello))#<class 'type'>

依次传入类名、继承的类、类的静态成员,可用于动态创建类

def fn(self, name='world'): # 先定义函数
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
metaclass 元类

metaclass允许创建类或者修改类,可以把类看成是metaclass创建出来的“实例”。
通常metaclass的类名总是以Metaclass结尾

# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

# 使用ListMetaclass定义类的时候需传入关键字参数metaclass:
class MyList(list, metaclass=ListMetaclass):
    pass

如此,则创建MyList时,要通过ListMetaclass.__new__()

  • 其中__new__()方法接收到的参数依次是:
    1. 当前准备创建的类的对象;
    2. 类的名字;
    3. 类继承的父类集合;
    4. 类的方法集合。
  • metaclass会隐式地继承到子类

相关文章

  • Python OOP

    断断续续写了几年程序,终于接触到面向对象的编程方式。本文记录与以往编程体验不同的地方 数据和逻辑被封装: 1. 操...

  • Python OOP

    面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分...

  • Python学习总结

    Python概述 数据类型 数据结构 OOP FP

  • python面向对象学习笔记-01

    学习笔记 # 0,OOP-Python面向对象 - Python的面向对象 - 面向对象编程 - 基础 -...

  • python面向对象程序设计(OOP)

    python面向对象程序设计(OOP) 类定义语法class className: 在python中可以...

  • 2018-04-14 开胃学习Python系列 - Object

    Python也是oop,虽然函数在Python生态系统中扮演重要角色。Python确实具有类别(classes) ...

  • python 类的细节

    下面我根据MARK Lutz的《Learning Python》中的“大师眼中的OOP”,列一些使用OOP的常见原...

  • python - OOP进阶

    前言 python的class有很多高级特性,除了OOP的三大特性,还在设计模式,自定制类和内存优化等都下了很多功...

  • Python oop(2)

    1.1应用:存放家具 对象之间的传递 1.1保护对象的属性 如果有一个对象,当需要对其进行修改属性时,有2种方法 ...

  • python - OOP基础

    前言 OOP的概念就不说了,想必都很了解了。 Oop的三大特点:继承、封装和多态。python作为动态语言的一种,...

网友评论

      本文标题:Python OOP

      本文链接:https://www.haomeiwen.com/subject/aslogctx.html