面向对象进阶

zhangly 2021-11-06 23:16:35
Categories: > Tags:

公共属性

使用id()可以查看,公共属性的地址是一样的。

Dog.d_type = '藏獒' 一旦更改,其他的实例对应属性也会随之更改。

class Dog:
	d_type = '京巴' # 公共属性, 类属性, 类变量

	def say_hi(self):
		print('hello, i am a dog, my type is ', self.d_type)

好处是可以节省内存空间。

重写父类的方法

继承父类方法后,需要重写父类方法,并调用父类方法的话。

可以用到super函数

class Animal:

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def eat(self):
        print('%s is eating..' % self.name)

class Person(Animal):

    def __init__(self, name, age, sex, lover):
        super(Person, self).__init__(name, age, sex)
        self.lover = lover

    def eat(self):
        # Animal.eat(self)
        super(Person, self).eat()
        print('人类不需要吃饭!')

p = Person('zhangly', 13, 'M', 'JayChou')
p.eat()

eat函数中的两种方式都可以行,如果是多继承,使用super的情况下,会一次执行eat

多继承的顺序

多继承在遇到重复的方法时,会采用以下两种查找方式:

py-class-learn-p1.png

在Python中,有两种类的写法,不同的写法采用的继承顺序不同。

class A: # 经典类
	pass

class B(object): # 新式类
	pass

Python2中:经典类采用的是深度优先,新式类采用广度优先

Python3中:都是采用广度优先

不过在实际测试后,Python使用的是一种C3的算法。

既不是深度优先,也不是广度优先。

类里面有一个方法可以打印出类的继承顺序:

class A:
	pass

class B(A):
	pass

class C(A):
	pass

class D(B, C):
	pass

print D.mro() # 这个方法

私有属性

class A:
    def __init__(self, name):
        self.name = name
        self.__age = 10 # 在变量名前加两个下划线

a = A('tutu')
print(a.__age) # 这样就无法获取

a._A__age = 30 # 可以通过这样的方式复制,或者获取(不推荐)

私有方法

class A:
    def __init__(self, name):
        self.name = name
        self.__age = 10

    def __check(self): # 同样是方法前加两个下划线
        print('here is check: ', self.name)

a = A('tutu')
a.__check() # 直接无法调用这个方法
a._A__check() # 可以通过这样的方式调用(不推荐)

多态

比如一个对象会有许多种表现形式,比如网站页面有个按钮(单选框,多选框,圆角点击按钮),

它们都一个onClick()方法。

这种多个对象公用一个接口,又表现的形态不一样的现象,就叫做多态。

下面是几个例子:

1.通过统一函数接口实现多态

class Dog(object):
    def sound(self):
        print('汪汪汪')

class Cat(object):
    def sound(self):
        print('喵喵喵')

def make_sound(animal_obj):
    """统一调用接口"""
    animal_obj.sound()

2.通过抽象类实现多态(常见用法)

class Document:
    def __init__(self, name):
        self.name = name

    def show(self):
        raise NotImplementedError('Subclass must implement abstract method!')

class Pdf(Document):

    def show(self):
        return 'Show pdf contents!'

class Word(Document):

    def show(self):
        return 'Show word contents!'

documents = [Pdf('Document1'),
             Pdf('Document2'),
             Word('Document3')
             ]

for doc in documents:
    print(doc.name, ': ', doc.show())

类变量和实例变量的区别

class Dog:

    name = 'stupid dog'

    def __init__(self, name):
        self.name = name

    @classmethod
    def eat(cls):
        print('dog {0} is eating..'.format(cls.name))

d = Dog('ly')
d.eat()

如果添加了classmethod,则代表函数下使用的是类变量。

这里举一个例子会用到类变量的情况:

有一个Student的类,每当实例化一个s1需要有一个变量记录学生的总数。

class Student(object):
	stu_num = 0
	def __init__(self, name):
		self.name = name
		Student.stu_num += 1 # 这样写作类变量

property相关

value.setter 可以set值

value.deleter 可以删除值

面向对象的反射

简而言之,可以通过字符串的形式来操作对象的属性。

比如一个例子,来判断对象是否有某个属性:

class Person:

    def __init__(self, name, age):
        self.name = name
        self.age = age

p1 = Person('zhang', 22)
if hasattr(p1, 'name2'):
    print('ok')
else:
    print('not ok')

除此之外还有几个方法:

getattr(obj, name) 获取某个属性
setattr(obj, name, value) 设置某个属性
delattr(obj, name) 删除某个属性

比如根据用户输入来获取要执行的方法:

user_command = input('>>:').sprip()
if hasattr(p1, user_command):
	func = getattr(p1, user_command)
	func()

动态的加载模块

使用__import()__ 方法。

Python官方推荐的方法是:

import importlib
importlib.import_module('os')

反射的高级用法

例子:

根据用户输入选取页面,一个常规写法。

# coding=utf-8

class User:
    def login(self):
        print('欢迎来到登陆页面')

    def register(self):
        print('欢迎来到注册页面')

    def save(self):
        print('欢迎来到存储页面')

while True:
    choose = input('>>>').strip()
    if choose == 'login':
        obj = User()
        obj.login()
    elif choose == 'register':
        obj = User()
        obj.register()
    elif choose == 'save':
        obj = User()
        obj.save()

如果使用反射:

u = User()
while True:
    choose = input('>>>').strip()
    if hasattr(User, choose):
        func = getattr(u, choose)
        func()

单例模式

__new__方法,在__init__方法之前执行。

class Student:

    def __init__(self, name):
        self.name = name
        print('init {0}'.format(self.name))

    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)

p = Student('Alex')

使用__new__方法来实现单例模式(不管实例化对象几次,都只有一个实例)

# coding=utf-8

class Printer(object):
    task = []
    instance = None

    def __init__(self, name):
        self.name = name

    def add_task(self, job):
        self.task.append(job)
        print('{0} 添加任务{1}到打印机,任务总数为:{2}')

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            obj = object.__new__(cls)
            cls.instance = obj
        return cls.instance

__call__方法

调用实例化对象,或者类的时候,执行的方法。

class School:

    def __init__(self, name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print(self.name)

s = School('ly')
s()

动态的创建一个类

使用type方法

dog_class = type('Dog', (object,), {'role': 'dog'})
d = dog_class()
print d.role

如果想要加上构造函数

def __init__(self, name, age):
	self.name = name
	self.age = age

dog_class = type('Dog', (object,), {'role': 'dog', '__init__': __init__})
d = dog_class('dahuang', 12)
print d.name

对象的判断

判断一个对象是否属于某个类

class Foo(object):
	pass

obj = Foo()
isinstance(obj, Foo)

判断一个类是否是另一个类的派生类

class Foo(object):
	pass

class Bar(Foo):
	pass

issubclass(Bar, Foo)

异常处理

while True:
	num1 = input('num1:')
	num2 = input('num2:')
	try:
		num1 = int(num1)
		num2 = int(num2)
		result = num1 + num2
	except Exception as e:
		print '出现异常,信息如下:'
		print e

几种用法:

# else 用法
try:
	print '一个程序'
except Exception:
	print '处理异常'
else:
	print '没有发生任何异常执行这里'

# finally 用法
try:
	print '一个程序'
except Exception:
	print '处理异常'
else:
	print '没有发生任何异常执行这里'
finally:
	print '不管有没有发生异常都执行'

主动触发异常,使用raise语法。

raise IndexError

自定义一个异常

class YoutubeConnectionError(BaseException):

    def __init__(self, msg):
        self.msg = msg

    def __str__(self):
        return self.msg

断言

判断代码是否符合执行预期

assert type(1) is int
assert len([1,2,3]) == 2