如何优雅地面向对象

概念

类(Class) :用来描述具备相同属性和方法的对象的集合。它定义了该集合中每一个对象所共有的属性和方法。其中的对象被称做类的实例。
实例/对象:经过类定义的初始化方法,赋予具体的值,成为一个"有血有肉的实体"。
实例化:类--->对象 的过程或操做。python

类变量:类变量是全部实例公有的变量。类变量定义在类中,但在方法体以外。
实例变量:定义在实例中的变量,只做用于当前实例。api

实例方法:至少有一个参数而且以实例对象(self)做为其第一个参数的方法。
静态方法:不须要实例化就能够由类执行的方法。
类方法:类方法是将类自己做为对象进行操做的方法。安全

封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制。
继承:即一个派生类(derived class)继承父类(base class)的变量和方法。
多态:根据对象类型的不一样以不一样的方式进行处理。数据结构

类的两种做用

1.属性引用(类名.属性)
class Person:   # 定义一个Person类
    role = 'person'  # 静态属性就是直接在类中定义的变量
    
    def __init__(self,name):
        self.name = name  # 每个角色都有本身的昵称;
        
    def walk(self):  # 动态属性就是定义在类中的方法
        print("person is walking...")

print(Person.role)  #查看人的role属性
print(Person.walk)  #引用人的走路方法,注意,这里不是在调用



2.实例化:类名加括号,自动触发__init__函数的运行,为每一个实例定制本身的特征
class 类名:
    类属性 = None
    def __init__(self,参数1,参数2):
        self.对象属性1 = 参数1
        self.对象属性2 = 参数2

    def 方法名(self):
        pass


实例/对象名 = 类名(参数)  # 对象就是实例,表明一个具体的东西
                          # 类名() : 类名+括号就是实例化一个类,至关于调用了__init__方法
                          # 括号里传参数,参数不须要传self,其余与init中的形参一一对应
                          # 结果返回一个对象
实例/对象名.对象属性   # 查看对象的属性
实例/对象名.方法名()      # 调用类中的方法

类的属性和方法

类属性/实例属性(也叫类变量、实例变量)

  1:实例属性:

    最好在__init__(self,...)中初始化

    内部调用时都须要加上self.

    外部调用时用instancename.propertyname

  2:类属性:

    在__init__()外初始化

    在内部用classname.类属性名调用

    外部既能够用classname.类属性名又能够用instancename.类属性名来调用

  3:私有属性:

    1):单下划线_开头:只是告诉别人这是私有属性,外部依然能够访问更改

    2):双下划线__开头:外部不可经过instancename.propertyname来访问或者更改

      实际将其转化为了_classname__propertyname

实例方法/静态方法/类方法

实例方法:类的实例方法由实例调用,至少包含一个self参数,且为第一个参数。执行实例方法时,会自动将调用该方法的实例赋值给self。self表明的是类的实例,而非类自己
静态方法:静态方法由类调用,无默认参数。将实例方法参数中的self去掉,而后在方法定义上方加上@staticmethod,就成为静态方法。它属于类,和实例无关。建议只使用类名.静态方法的调用方式。(虽然也可使用实例名.静态方法的方式调用)函数

class Foo:

    @staticmethod
    def static_method():
        pass

#调用方法
Foo.static_method()

类方法:类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类自己,相似self)参数。执行类方法时,自动将调用该方法的类赋值给cls。建议只使用类名.类方法的调用方式。(虽然也可使用实例名.类方法的方式调用)code

class Foo:

    @classmethod
    def class_method(cls):
        pass

Foo.class_method()

综合例子:orm

class Foo: 

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

    def ord_func(self):
        """定义实例方法,至少有一个self参数 """
        print('实例方法')

    @classmethod
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法') 

# 调用实例方法
f = Foo("Jack")
f.ord_func()
Foo.ord_func(f) # 请注意这种调用方式,虽然可行,但建议不要这么作!

# 调用类方法
Foo.class_func()
f.class_func()  # 请注意这种调用方式,虽然可行,但建议不要这么作!

# 调用静态方法
Foo.static_func()
f.static_func() # 请注意这种调用方式,虽然可行,但建议不要这么作!

封装、继承、多态

封装

封装是指将数据与具体操做的实现代码放在某个对象内部,使这些代码的实现细节不被外界发现,外界只能经过接口使用该对象,而不能经过任何形式修改对象内部实现,正是因为封装机制,程序在使用某一对象时不须要关心该对象的数据结构细节及实现操做的方法。使用封装能隐藏对象实现细节,使代码更易维护,同时由于不能直接调用、修改对象内部的私有信息,在必定程度上保证了系统安全性。类经过将函数和变量封装在内部,实现了比函数更高一级的封装。对象

继承

Python3的继承机制继承

  • 子类在调用某个方法或变量的时候,首先在本身内部查找,若是没有找到,则开始根据继承机制在父类里查找。
  • 根据父类定义中的顺序,以深度优先的方式逐一查找父类!
    继承参数的书写有前后顺序,写在前面的被优先继承。

多态

多态指的是一类事物有多种形态索引

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')
        
peo=People()
dog=Dog()
pig=Pig()

# peo、dog、pig都是动物,只要是动物确定有talk方法
# 因而咱们能够不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更进一步,咱们能够定义一个统一的接口来使用
def func(obj):
    obj.talk()  # 调用的逻辑都同样,执行的结果却不同

talk(peo)
talk(dog)
talk(pig)

特殊成员和魔法方法

__init__ :      构造函数,在生成对象时调用
__del__ :       析构函数,释放对象时使用
__repr__ :      打印,转换
__setitem__ :   按照索引赋值
__getitem__:    按照索引获取值
__len__:        得到长度
__cmp__:        比较运算
__call__:       调用
__add__:        加运算
__sub__:        减运算
__mul__:        乘运算
__div__:        除运算
__mod__:        求余运算
__pow__:        幂

经常使用以下

1.__getitem__()、__setitem__()、__delitem__()
a = 标识符[] :   执行__getitem__方法
标识符[] = a  :   执行__setitem__方法
del 标识符[] :   执行__delitem__方法

class Foo:

    def __getitem__(self, key):
        print('__getitem__',key)

    def __setitem__(self, key, value):
        print('__setitem__',key,value)

    def __delitem__(self, key):
        print('__delitem__',key)


obj = Foo()

result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = 'jack'      # 自动触发执行 __setitem__
del obj['k1']           # 自动触发执行 __delitem__

2.__str__(),__repr__()改变对象的字符串显示
3.__format__自定制格式化字符串
4.__del__()析构方法,当对象在内存中被释放时,自动触发执行
5.__new__单例模式
6.__call__()对象后面加括号,触发执行
7.__len__() len()时执行
8.__module__ 表示当前操做的对象在属于哪一个模块
9.__class__ 表示当前操做的对象属于哪一个类
10.__dict__列出类或对象中的全部成员!很是重要和有用的一个属性
11.__iter__这是迭代器方法!列表、字典、元组之因此能够进行for循环,内部定义了 __iter__()这个方法
12.__slots__限制实例能够添加的变量
...

反射

反射(或自省)主要是指程序能够访问、检测、和修改它自己状态或行为的一种能力,在python中,经过字符串的形式操做对象相关的属性,python中的一切事物都是对象,即均可以使用反射。
反射4个内置函数分别为:getattr、hasattr、setattr、delattr 获取成员、检查成员、设置成员、删除成员

hasattr:hasattr(object,name)判断一个对象是否有name属性或者name方法。有就返回True,没有就返回False 
getattr:获取对象的属性或者方法,若是存在则打印出来。hasattr和getattr配套使用,须要注意的是,若是返回的是对象的方法,返回出来的是对象的内存地址,若是须要运行这个方法,能够在后面添加一对()   
setattr:给对象的属性赋值,若属性不存在,先建立后赋值  
delattr:删除该对象指定的一个属性
  • isinstance(obj,cls)检查是否obj是不是类 cls 的对象
  • issubclass(sub, super)检查sub类是不是 super 类的派生类

其余

关于self

self:在实例化时自动将对象/实例自己传给__init__的第一个参数,你也能够给他起个别的名字。
实例(对象)只有一种做用:属性引用

关于super

若是子类中实现了调用父类的方法:        
    在类内:super(子类,self).方法名()  supper().__init__(参数) 
    在类外:super(子类名,对象名).方法名()

零碎

getattr(obj,"name") = obj.name
Python内置的@property装饰器就是负责把一个方法变成属性调用
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,
可是若是使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
相关文章
相关标签/搜索