Python_面向对象

面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

  1. 数据封装、继承和多态是面向对象的三大特点
  2. 面向对象的设计思想是抽象出Class,根据Class创建Instance
  3. 面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。

定义类及初始化变量:

class Student(object):
    # 在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。
    # 通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去
    def __init__(self, name, score):
        self.name = name
        # 添加下划线可以让变量私有,禁止从外部访问他
        # 如果还想修改这个变量 可以提供外部方法
        self._score = score

    def print_score(self):
        print('个人信息:%s: %s' % (self.name, self._score))

    def get_grade(self):
        if self._score >= 90:
            return 'A'
        elif self._score >= 60:
            return 'B'
        else:
            return 'C'

    def set_score(self, score_up):
        self._score = score_up

在调用的时候就要先将类实例化,然后调用相应函数:

student = Student('王八羔子', 80)
student.print_score()
# 如果给外部变量添加 下划线“_”就可以让该变量变成一个私有变量而且从外部无法访问
# 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name
# 是因为Python解释器对外把_score变量改成了_Student__score,所以,仍然可以通过_Student__score来访问_name变量:
# 强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。
# Python本身没有任何机制阻止你干坏事,一切全靠自觉
student.name = '张三'
student.print_score()
print("这个家伙的成绩标准是:" + student.get_grade())
student.set_score(50)
student.print_score()
print("这个家伙的成绩标准是:" + student.get_grade())
打印结果:
个人信息:王八羔子: 80
个人信息:张三: 80
这个家伙的成绩标准是:B
个人信息:张三: 50
这个家伙的成绩标准是:C

类的继承和多态

class Animal(object):
    def run(self):
        print('Animal is running...')
# 继承
class Dog(Animal):
    def run(self):
        print('Dog is running...')
# 继承        
class Cat(Animal):
    def run(self):
        print('Cat is running...')
 调用:
dog = Dog()
dog.run()
cat = Cat()
cat.run()
打印结果:
			Dog is running...
			Cat is running...

python中的鸭子类型:
#对于静态语言(例如Java)来说,如果需要传入Animal类型,
#则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
#对于Python这样的动态语言来说,则不一定需要传入Animal类型。
#我们只需要保证传入的对象有一个run()方法就可以了:
#这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,
#一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子
#Python的“file-like object“就是一种鸭子类型。对真正的文件对象,
#它有一个read()方法,返回其内容。但是,许多对象,只要有read()方法,
#都被视为“file-like object“。许多函数接收的参数就是“file-like object“,
#你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

比如我们定义如下函数:
def run_twice(an):
    if hasattr(an, 'run'):
        an.run()
    return None
比如上述函数,根据函数体我们知道他接收一个示例,这个示例有一个run方法
我们在上述定义的Animal、Dog、Cat都有一个run函数,我们现在在定义一个Person类:
他同样具有所谓的run方法
class People():
    def run(self):
        print('People is running...')
那么如下调动都是没有问题的:
run_twice(dog)
run_twice(cat)
run_twice(people)

#判断对象是否是函数 使用types模块中定义的常量 import types
#type()函数:判断对象类型
#基本类型 函数 类都可以使用:
print(type(123))
print(type(‘编的跟真的一样’))
print(type(True))
print(type(dog))
print(type(cat))
print(type(abs))
打印结果:
<class ‘int’>
<class ‘str’>
<class ‘bool’>
<class ‘module.test_class.Dog’>
<class ‘module.test_class.Cat’>
<class ‘builtin_function_or_method’>
#判断class的类型,使用isinstance()函数,返回True或者False
print(isinstance(dog, Dog))
print(isinstance(dog, Animal))
print(isinstance(cat, Cat))
print(isinstance(cat, Animal))
print(isinstance(dog, Cat))
#判断一个变量是否是某些类型中的一种
print(isinstance((1, 2, 3), (list, tuple)))
print(isinstance([1, 2, 3], (list, tuple)))

#获得一个对象的所有属性和方法,使用dir()函数
print(dir(dog))
print(dir(‘str’))

 # Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Teacher(object):
    # Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
    __slots__ = ('name', 'age')

    # __slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
    def __init__(self, name, age):
        if isinstance(name, str):
            self.name = name
        if isinstance(age, int):
            self.age = age
        elif isinstance(age, float):
            self.age = int(age)
        elif isinstance(age, str):
            self.age = int(age)
        else:
            self.age = 18

    def print_other(self):
        print('教师个人信息:%s: %s' % (self.name, self.age))

------------分割线----------------

在介绍上述__slots__变量的时候我们先插播getattr()、setattr()以及hasattr()这几个函数
hasattr():对象是否有某个属性或者方法 返回True或者False
hasattr(obj, 'y') # 有属性'y'吗?
setattr():为对象设置一个属性
setattr(obj, 'y', 19) # 设置一个属性'y'
getattr() 获取对象的属性或者方法
getattr(obj, 'z') # 获取属性'z'
getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn,fn指向obj.power
if not hasattr(student, 'sex'):
    student.sex = '男'
print(student.sex)

------------分割线----------------

teacher = Teacher("二狗子", 30)
teacher.print_other()
# teacher.sex = '男'  # 错误 在类中通过__slots__已经限制了只能添加name age两个属性

通过Python内置的@property装饰器把一个方法变成属性调用

class Tables(object):
    # 把一个getter方法变成属性,只需要加上@property就可以了,
    # 此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

    # 只读属性
    @property
    def grade(self):
        if self._score >= 90:
            return 'A'
        elif self._score >= 60:
            return 'B'
        else:
            return 'C'
调用:
tables = Tables()
tables.score = 80  # OK,实际转化为tables .set_score(80)
print('成绩是:' + str(tables.score) + '----所在级别:' + tables.grade) # OK,实际转化为tables.get_score()

不同于java中的单继承,python中支持多继承 ,也就是说一个类可以有多个父类或者基类

# 本示例主要用来讲解多重继承
class Animal2(object):
    def cry(self):
        print("我是一个会叫的小动物")


# 哺乳类
class Mammal(Animal2):
    def temperature(self):
        print('我是一只恒温哺乳动物')


# 鸟类
class Bird(Animal2):
    def temperature(self):
        print('我是一只恒温鸟类')


# 可以跑的动物
class Runnable(object):
    def run(self):
        print('瞎几把跑吧...')


# 可以飞的动物
class Flyable(object):
    def fly(self):
        print('我要飞得更高...')


# 各种动物:
class Dog2(Mammal, Runnable):
    pass


class Cat2(Mammal):
    pass


# 各种鸟
class Parrot(Bird,Flyable):
    pass


class Ostrich(Bird):
    pass
    
 调用:
 
 dog2 = Dog2()
cat2 = Cat2()
parrot = Parrot()
ostrich = Ostrich()
dog2.cry()
dog2.temperature()
dog2.run()
print('----------------------------------')
cat2.cry()
cat2.temperature()
print('----------------------------------')
parrot.cry()
parrot.temperature()
parrot.fly()
print('----------------------------------')
ostrich.cry()
ostrich.temperature()
打印结果:
我是一个会叫的小动物
我是一只恒温哺乳动物
瞎几把跑吧...
----------------------------------
我是一个会叫的小动物
我是一只恒温哺乳动物
----------------------------------
我是一个会叫的小动物
我是一只恒温鸟类
我要飞得更高...
----------------------------------
我是一个会叫的小动物
我是一只恒温鸟类

----------分割线-------------

__iter__
如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,
该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法
拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1  # 初始化两个计数器

    def __iter__(self):
        return self  # 示例本身就是迭代对象 故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b  # 计算下一个值
        if self.a > 100:
            raise StopIteration
        return self.a

    # 如果想要实现像list那样按照下标取出元素,需要实现__getitem__()方法:
    # 传入的参数可能是个int 也可能是个slice切片
    def __getitem__(self, item):
        if isinstance(item, int):
            a, b = 1, 1
            for x in range(item):
                a, b = b, a + b
            return a
        elif isinstance(item, slice):
            start = item.start
            stop = item.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L

# 定制类
# 一个类作用于for循环
for n in Fib():
    print(n)
# 根据下标取出元素
f = Fib()
print(f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9])
print(f[:10])  # 传入的不是整数 是个切片
print(f[:10:2])  #没有对step参数作处理:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
其实这个也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。
此外,如果把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,例如str。
与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。
最后,还有一个__delitem__()方法,用于删除某个元素。
总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,
这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

---------------------分割线--------------------------------

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错
但是__getattr__()方法,可以动态返回一个属性
class Student(object):
    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:
s = Student()
s.name
s.score
打印结果
'Michael'
 99

返回函数也可以
class Student(object):
    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
调用:
print(s.age())
只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。
这种功能可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段,可以针对完全动态的情况作调用

利用完全动态的__getattr__,我们可以写出一个链式调用:
class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__
调用:
ch = Chain().status.user.timeline.list
print(ch)
返回结果
'/status/user/timeline/list'

--------------------------分割线-------------------------------

任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用
class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)
调用:
s = Student('Michael')
print(s())#self参数不要传入
打印结果:
My name is Michael.

---------------------------分割线-----------------------------------

枚举类
from enum import Enum, unique

# 枚举类
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
class WeekDay(Enum):
    Sun = 0  # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6


# 调用方式
print('--------------', WeekDay.Sun)
print('--------------', WeekDay['Sun'])
print('--------------', WeekDay.Sun.value)
# 实体类中如果某些属性的取值是固定的话可以使用定义枚举类赋值

--------------------------分割线-----------------------------------

# type()
# 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
# 在静态语言中 不论是函数 类还是变量 必须先定义后使用
# 但是在python中 type()函数既可以返回一个对象的类型,又可以创建出新的类型,
# 比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:
def fn(self, name='world'):  # 先定义函数
    print('Hello %s' % name)


# 要创建一个class对象,type()函数依次传入3个参数:
# class的名称;
# 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
#(第二个参数是个父类的tuple集合,一个元素的tuple要在括号中加,)
# class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
Hello = type('Hello', (object,), dict(hello=fn))  # 创建hello class
h = Hello()
h.hello()
print(type(Hello))
print(type(h))
打印结果:
Hello world
<class 'type'>
<class '__main__.Hello'>

最后:
metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

# 定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:
# metaclass是类的模板,所以必须从`type`类型派生:
# 给我们自定义的MyList增加一个add方法:
class ListMetaclass(type):
    # 当前准备创建的类的对象;
    # 类的名字;
    # 类继承的父类集合;
    # 类的方法集合。
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)


# 当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,
# 要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
class MyList(list, metaclass=ListMetaclass):
    pass
调用:
# 使用metaclass来创建类
L = MyList()
L.add(1)
print(L)
结果:
[1]

需要通过metaclass修改类定义的。ORM就是一个典型的例子,详细了解请查看相关资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值