python基础(十一)面向对象和类

会有一股神秘感。 提交于 2019-12-24 10:42:18

面向对象

面向对象,简单来说用类对一切对象进行描述的一种编程思维和方法.在这里就不多做介绍(因为我也只是意会到一点点).根据面向对象的概念,我们需要注意的几个概念:

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.

  • 对象(Object):通过类定义的数据结构实例.对象包括两个数据成员(类变量和实例变量)和方法。

  • 类变量:类变量在整个实例化的对象中是公用的,类变量定义在类中且在函数体之外,类变量通常不作为实例变量使用.

  • 实例变量:定义在方法中的变量,只作用于当前实例.

  • 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。

  • 方法:类中定义的函数.

  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法.也称为父类和子类

  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写.

  • 实例化:创建一个类的实例,类到具体对象的过程

  • 属性: 不管是变量或者是方法,都称为属性.变量一般称为静态属性,方法称为动态属性

面向对象的三大特性

1, 封装: 把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏.

2, 继承: 让不同类型的对象获得其它类型对象的属性和方法.

3, 多态: 实现接口重用, 即可以不同类型对象使用相同的方法,也可以一个对象的相同方法在不同情形有不同的表现形式

类的封装,基本构成和使用

1, 创建类,先来看例子

class Test(object):
    '''class_doc'''
    class_var = 'class_var'

    def class_func(self):
        obj_var = 'obj_var'
        print(obj_var)
class       |   定类的关键字通常类的首字母使用大写
object      |   新式类的所有类的基类,经典类和新式类在后面阐述,python3中建议使用这种形式定义类
class_doc   |   类的文档,可以通过__doc__方法查看
obj_var     |   实例变量,需要对类进行实例化生成对象后,才能通过对象是属于具体实例的.静态属性
def         |   定义类方法的关键字,和普通函数作用相同.动态属性
self        |   类方法必须有的参数,代表的是实例自身
class_var   |   类变量,可以在外部直接调用,不需要先进行实例话

2, 实例化,在python中使用类似函数调用的方式实例化类,并没有其它语言中的特殊关键字

t1 = Test()
#t1 就是类Test的具体对象

3, 访问属性,同样是上例子

class Test(object):

    class_var = 'class_var'

    def obj_func(self):
        obj_var = 'obj_var'
        print(obj_var)

t1 = Test()
Test.class_var = 'modify from Test'
print(Test.class_var)
#print(Test.obj_func())

t1.class_var = 'modify from t1'
print('Test:',Test.class_var)
print(t1.class_var)
print(t1.obj_func())
#print(t1.obj_var)

我们可以发现几个情况:

  • (1)类变量不需要实例化可以访问,实例在实例化后会共用类变量,
  • (2)不能通过实例修改类变量,修改的自身的那份类变量.相当于新生成了一个实例变量.
  • (3)类里定义的方法通常没有实例化,是不能访问.
  • (4)类方法里定义的实例变量是不能在外部直接调用的.

4, 类的内置方法,可以查看一些类的基本信息.(有些属性实例不能使用.)

__init__        |   构造方法
__del__         |   析构函数
__name__        |   查看类名
__dict__        |   查看类属性成员 
__doc__         |   查看类文档
__class__       |   查看对象所属的类
__module__      |   查看对象所在模块
__bases__       |   查看基类
__mro__         |   查看继承顺序
__call__        |   让实例化的对象可调用(即可以加执行符号())
__str__         |   打印对象是,返回这个方法的值,通常用在实例化的对象定制信息
__getitem__     |   见第9小点
__setitem__     |   见第9小点
__delitem__     |   见第9小点
__new__         |   实例化方法
__slots__       |   限制类属性
__metaclass__   |   查看元类

5, 构造函数,类的实例化时执行的函数,通常用来实现对象的初始化._init__

class Test():
    name = 'class_var'
    def __init__(self,name,choice=None):
        self.name = name
        self.choice = choice

        if self.choice == 'yes':
            self.say_hello()
        else:
            self.other()

    def say_hello(self):
        print('Welcome,%s'%self.name)
    def other(self):
        print('No choice')

t1 = Test('sylar','yes')
print(t1.name)
#Test.name
t2 = Test('tom')

通过这个例子可以发现,构造函数中定义的属性是针对实例本身,并且也印证了类中的关键字self是指对象而不是类

6, 析构函数,__del__()也是可选的,如果不提供,则Python 会在后台提供默认析构函数.在对象销毁时自动执行的函数,显示的执行可以使用del obj

import time

class del_test(object):

    def say_bye(self):
        print('see you next time')

    def __del__(self):
        self.say_bye()

o1 = del_test()
del o1
time.sleep(3)
o2 = del_test()

7, 私有属性,无法在外部调用,但是可以在内部调用,从而隐藏某些属性或方法.

class Test(object):
    def __init__(self,name,id_status):
        self.name = name
        self.__status = id_status

    def show_status(self):
        print(self.__status)

t1 = Test('sylar','some info')
t1.show_status()
t1.__status

单下划线、双下划线、头尾双下划线说明:

__foo__: 定义的是特殊方法,一般是系统定义名字,类似 init() 之类的.

_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *

__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了.

8, __call__方法,让不可执行的对象变的可执行

class Animal(object):
    def __call__(self, *args, **kwargs):
        print('call func')

t = Animal()
t()
Animal()()

9, __getitem__,__setitem__,__delitem__.用类模拟类似字典的操作

class Test(object):
    def __getitem__(self,key):
        print('__getitem__:',key)
    def __setitem__(self, key, value):
        print('__setitem__:',key,value)
    def __delitem__(self, key):
        print('__delitem__:',key)
t1 = Test()
t1['name']
t1['name'] = 'sylar'
del t1['name']

类的继承

1, 继承,代码重用,派生类(子类)会完全继承基类(父类)的所有属性.

class Base_Class(object):
    def __init__(self,name):
        self.name = name
        self.say_hi()
    def say_hi(self):
        print('Welcome, %s!'%self.name)
    def call_me(self):
        print("%s"%self.name)

class Sub_Class(Base_Class):
    pass

t1 = Sub_Class('sylar')
t1.call_me()

2, 方法重写: 当子类中定义的属性或方法和父类重名时,子类会覆盖父类中的定义.有些时候需要父类的方法,又需要做一些改动.可以直接在子类中调用父类的方法然后再添加新的代码,或者使用super函数.(建议使用super)

class Base_Class(object):
    def __init__(self,name):
        self.name = name
    def call_me(self):
        print("%s"%self.name)

class Sub_Class(Base_Class):
    def call_me(self):
        #super(Sub_Class,self).call_me()
        Base_Class.call_me(self)
        print('modify for Sub_Class')


t1 = Sub_Class('sylar')
t1.call_me()

3, 多继承顺序,又分为经典类和新式类,在python3中默认都是使用广度优先. 使用__mro__方法可以查看继承的查找顺序

class Base_A:
    def __init__(self):
        print('from A')

class Base_B(Base_A):
    pass

class Base_C(Base_A):
    def __init__(self):
        print('from C')

class Sub_Class(Base_B,Base_C):
    pass

t1 = Sub_Class()
print(Sub_Class.__mro__)

这段经典类的代码在python2X 输出为'from A'深度优先,在python3x输出为'from C'广度优先
python2 的新式类使用的广度优先

类的多态

接口重用,使用统一的接口对子类的方法进行调用

class Animal(object):
    @staticmethod
    def run_func(obj):
        obj.run()

class Cat(Animal):
    def run(self):
        print('Cat is running')

class Mouse(Animal):
    def run(self):
        print('Mouse is running')

c1 = Cat()
m1 = Mouse()
Animal.run_func(c1)
Animal.run_func(m1)

类内置装饰器

1, 静态方法 staticmethod不能访问类和实例属性,相当于和类本身没有什么关系,只是需要通过类名来调用这个方法.而且实例化后self也不会被传入到这个方法中.前面的多态就是一种使用场景.

2, 类方法 classmethod只能访问类变量不能访问实例变量

class Animal(object):
    name = 'Tom'
    def __init__(self):
        self.name = 'Jerry'

    @classmethod
    def run_func(self):
        print('%s is running'%self.name)

t = Animal()
t.run_func()

3, 属性方法 把一个方法当作静态属性来使用

单独使用,不能传参数

class Animal(object):
    def __init__(self):
        self.name = 'Jerry'

    @property
    def run_func(self):
        print('%s is running'%self.name)

t = Animal()
t.run_func

配合setter接受赋值,实现类似传入参数的功能

class Animal(object):
    def __init__(self):
        self.__who = None

    @property
    def run_func(self):
        print('%s is running'%self.__who)

    @run_func.setter
    def run_func(self,who):
        #print('set to name',who)
        self.__who = who

配合deleter删除,默认property装饰器转换过来的属性是不能删除的.如果要删除就要使用deleter

class Animal(object):
    def __init__(self):
        self.__who = None

    @property
    def run_func(self):
        print('%s is running'%self.__who)

    @run_func.setter
    def run_func(self,who):
        #print('set to name',who)
        self.__who = who
    @run_func.deleter
    def run_func(self):
        del self.__who
        
t = Animal()
t.run_func = 'Tom'
t.run_func
del t.run_func

类的定制(元类)和type

由于python是解释型语言,所以函数和类的定义不是编译时定义的,而是运行时动态创建的.在python中所有的类都是继承object,并由type方法生成的.type接收三个参数:类名,继承的父类元组,需要绑定的函数. 就可以生成一个类

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

def say_hi(self):
    print('Hello %s'%self.name)

Test = type('Test', (object,), {'say_hi' : say_hi, '__init__' : my_init,})
t = Test('sylar')
t.say_hi()

1, __new__类的实例化方法,在所有类实例化的时候都是通过new方法

class MyClass(object):
    def __init__(self):
        print('some code in init!')

    def __new__(cls, *args, **kwargs):
        print('some code in new!')
        return object.__new__(cls)

t1 = MyClass()
  • new是执行在init之前的,并且通过return触发init的执行
  • return object.__new__(cls)相当于super去继承但这时候还没生成self,
  • cls相当于self,指代MyClass这个类的自身

2,metaclass元类,创建类的类,类是metaclass创建的实例.在python中自定义类需要先创建metaclass再创建类.python3和python2的语法有所改变

python3

def my_add(self,value):
    self.append(value)

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['my_add'] = my_add    #这里也可以使用lambda
        return type.__new__(cls, name, bases, attrs)

class MyList(list,metaclass=ListMetaclass):
    pass 

l1= MyList()
l1.my_add(1)
l1.my_add(2)
print(l1)

python2

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class MyList(list):
    __metaclass__ = ListMetaclass 

属性锁定

前面已经说了python类的属性可以动态创建的,为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性

class Test():
    #__slots__ = ('name','age')
    def __init__(self,name,age):
        self.name = name
        self.age = age
t = Test('sylar',18)
t.job = 'it'

反射

判断类或实例是否有相应的属性,简单来说就是通过字符串来操作类.

hasattr(obj,name_str),判断对象里是否有对应字符串的属性

class Test():
    #__slots__ = ('name','age')
    def __init__(self,name,age):
        self.name = name
        self.age = age

t = Test('sylar',18)
choice = input()
res = hasattr(t,choice)
print(res)

getattr(obj,name_str),根据字符串获取对象相应的属性或是属性的内存地址

class Test():
    #__slots__ = ('name','age')
    def __init__(self,name,age):
        self.name = name
        self.age = age

t = Test('sylar',18)
choice = input()
res = getattr(t,choice)
print(res)

setattr(obj,name_str,attrs),根据字符串给对象设置相应的属性

class Test():
    #__slots__ = ('name','age')
    def __init__(self,name,age):
        self.name = name
        self.age = age

t = Test('sylar',18)
choice = input()
setattr(t,choice,'Tom')
v = getattr(t,choice)
print(t.name,v)

delattr(obj,name_str),删除字符串对应的属性

class Test():
    #__slots__ = ('name','age')
    def __init__(self,name,age):
        self.name = name
        self.age = age

t = Test('sylar',18)
choice = input()
delattr(t,choice)
print(t.name)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!