Python OOP

2019-12-16  本文已影响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('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
>>> hasattr(obj, 'x')
True
>>> obj.x
9
>>> getattr(obj, 'y') # 获取属性'y' 没有则报错
>>> getattr(obj, 'y', 20) # 获取属性'y' 没有则使用默认值20
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
del jack.score
私有成员

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

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

特殊成员

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

>>> len('ABC')
3
>>> 'ABC'.__len__()
3
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
class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
s.age()#25
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__ 可以获取类和实例的属性字典,以查看该属性是属于类还是实例

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__

限制成员读写
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

继承和多态

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

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

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

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

JS(TS)、C#、Python中类继承时父类构造函数的调用情况

枚举类

通过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__()

上一篇 下一篇

猜你喜欢

热点阅读