首页 > 代码库 > Python7 面向对象

Python7 面向对象

面向对象编程

OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

面向对象的几个核心特性如下

class类

一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法

object对象

一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同

Encapsulation 封装

在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法

Inheritance 继承

一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承

Polymorphism 多态

多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定

类的实例化过程

class Dog(object):

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

    def sayhi(self):
        print(hello,I am a dog,my name is , self.name)

d = Dog(LiChuang, 京巴)   # 实例化一个对象
d.sayhi()   # 调用class中的方法

d = Dog(‘LiChuang‘, ‘京巴‘)相当于d = Dog(d, ‘LiChuang‘, ‘京巴‘),self就是实例本身实例化时python会自动把这个实例本身通过self参数传进去

上面的这个__init__()叫做初始化方法(或构造方法), 在类被调用时,这个方法(虽然它是函数形式,但在类中就不叫函数了,叫方法)会自动执行,进行一些初始化的动作

封装

class Student(object):

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

    def print_score(self):
        print("%s:%s" % (self.name,self.score))

这里传入两个参数分别为name、socre,实例化后可以调用print_score方法去打印,使用者不需要知道内部复杂的逻辑,类将这里面复杂的逻辑进行了封装。

访问限制

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。

但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性

class Student(object):
    
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print("%s:%s" % (self.name, self.score))


s1 = Student("Jack", 95)    # 实例化一个学生s1
s1.name = "Tom" # 修改实例的name变量为Tom
s1.print_score()  # 打印结果为Tom:95

如果要让内部属性不能被外部访问,可以在属性的前面加两个下划线self.__name = name

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print("%s:%s" % (self.__name, self.__score))


s1 = Student("Jack", 95)    # 实例化一个学生s1
s1.__name = "Tom" # 修改实例的name变量为Tom
s1.print_score()  # 打印结果为Jack:95

强制访问某个私有属性  s1._Student__name 

类的继承

class Animal(object):

    def run(self):
        print("Animal is running...")

class Dog(Animal):
    pass
     
d1 = Dog()
d1.run()

上面这段代码,有两个类,Animal和Dog类,对于Dog来说Animal是它的父类,Dog是Animal的子类,Dog没有定义任何方法,但由于继承了Animal,所以我们可以调用父类中的run方法,当然Dog类也可以有自己的run方法

class Animal(object):

    def run(self):
        print("Animal is running...")


class Dog(Animal):

    def run(self):
        print(Dog is running...)

d1 = Dog()
d1.run()

当父类和子类同时有相同的方法时,子类的方法就将父类的方法覆盖了

继承构造函数有两种方法,新式类和经典类略有不同

class Person(object):   # 新式类
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def talk(self):
        print("%s is %s years old,talking language..." % (self.name, self.age))
        
        
class Chinese(Person):
    
    def __init__(self, name, age, language):
        super(Chinese, self).__init__(name, age)    # 新式类继承
        self.language = language
        
    def talk(self):
        print("%s is %s years old,talking %s..." % (self.name, self.age, self.language))
        
p1 = Chinese("张三", 28, "中文")
p1.talk() 


class Person:   # 经典类
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def talk(self):
        print("%s is %s years old,talking language..." % (self.name, self.age))
        
        
class Chinese(Person):
    
    def __init__(self, name, age, language):
        Person.__init__(self, name, age)            # 经典类继承
        self.language = language
        
    def talk(self):
        print("%s is %s years old,talking %s..." % (self.name, self.age, self.language))
        
p1 = Chinese("张三", 28, "中文")
p1.talk()

类的多态

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...")
         
def run_twice(animal):
    animal.run()
    animal.run()
     
run_twice(Animal())
run_twice(Dog())
run_twice(Cat())

多态的好处显而易见,run_twice()执行了一个动物的run方法,动物可以有很多,每种动物都继承父类Animal,是否重新定义run方法都会先继承,这样不管是哪种动物,执行run_twice函数都会正常运行,每种动物有自己的run方法,是Animal的一个多种形态

新式类和经典类

#新式类
class Person(object):
    pass

#经典类
class Person:
    pass

#python3
#新式类
# D->B-C-A  广度查询
#经典类
# D->B-C-A  广度查询

#python2
#新式类
# D->B-C-A  广度查询
#经典类
# D->B-A-C  深度查询

类的属性

class Student(object):

    name = "haha"

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

s = Student("Tom")
print(s.name)   # Tom
del s.name
print(s.name)   # haha

上述代码,name="haha"为类中的属性,属于类;self.name = name是实例属性,属于实例

所以第一打印时是Tom,而删除s.name实际是删除了实例中的name属性,第二次打印,打印的类中的name属性

staticmethod、classmethod、property

@staticmethod

静态方法,只是名义上归类管理,实际上在静态方法里访问不了类或实例中的任何属性,直接使用类去调用,和使用函数一样

class Person(object):
    
    def __init__(self, name):
        self.name = name

    @staticmethod
    def print_info():
        print("test print_info")

Person.print_info()

@classmethod

类方法,只能使用类变量,不能使用实例的变量

class Person(object):

    name = "xxx"

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

    @classmethod
    def print_info(cls):
        print("My name is %s" % cls.name)
        
Person.print_info()

@property

将一个类中的方法变成属性去调用

class Person(object):

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

    @property                # 将一个类中的方法变成属性去调用
    def print_info(self):
        print("I\‘m %s,%s years old" % (self.name, self.age))

    @print_info.setter       # 修改这个属性的参数
    def print_info(self, age):
        self.age = age

    @print_info.deleter      # 删除这个属性的参数
    def print_info(self):
        del self.age

p = Person(Tom)   # 实例一个对象
p.print_info    # 普通调用

# 修改参数
p.print_info = 28   # 多个参数可以参考*args **kwargs
p.print_info

# 删除参数
del p.print_info
p.print_info    # 报错

类的特殊成员方法

__doc__方法,打印类的描述信息

class Person(object):
    """类的描述信息"""
    pass

p = Person() print(p.__doc__)

__module__,打印当前操作的对象在哪个模块

class Person(object):
    pass

p = Person()
print(p.__module__)

__class__,打印当前操作的对象属于哪个类

class Person(object):
    pass

p = Person()
print(p.__class__)

__init__,构造方法,实例化时自动创建

__del__,析构方法,当对象在内存中被释放时,自动触发

__call__,对象后面加括号触发

class Foo(object):

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print("__call__")
        
f = Foo()
f()

__dict__,查看类或对象中的所有成员

class Foo(object):

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

    def info(self):
        print(self.name)

f = Foo("Tom")
print(Foo.__dict__)
print(f.__dict__)

__str__,如果定义了__str__方法,打印对象时,输出该方法的返回值

class Foo(object):

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

    def info(self):
        print(self.name)

    def __str__(self):
        return "test"
    
f = Foo("Tom")
print(f)

__getitem__、__setitem__、__delitem__,用于索引操作,获取、设置、删除数据

class Foo(object):

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

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

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

f = Foo()
f["key1"]
f["key1"] = "Tom"
del f["key1"]

__new__ \ __metaclass__

在Python中一切皆对象,实例通过类的构造函数去创建,而类本身由什么去创建呢?type

创建一个类的普通方法

class Foo(object):

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

f = Foo("Tom")

特殊方法

def info(self):
    print(self.name)

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

Foo = type("Foo", (object,), {"info": info, "__init__": __init__})
f = Foo("Tom")
f.info()

类创建的过程

类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。

class MyType(type):
    def __init__(self, child_cls, bases=None, dict=None):
        print("--MyType init---", child_cls, bases, dict)
        # super(MyType, self).__init__(child_cls, bases, dict)

    # def __new__(cls, *args, **kwargs):
    #     print("in mytype new:", cls, args, kwargs)
    #     type.__new__(cls)

    def __call__(self, *args, **kwargs):
        print("in mytype call:", self, args, kwargs)
        obj = self.__new__(self, args, kwargs)

        self.__init__(obj, *args, **kwargs)


class Foo(object, metaclass=MyType):  # in python3
    # __metaclass__ = MyType #in python2

    def __init__(self, name):
        self.name = name
        print("Foo ---init__")

    def __new__(cls, *args, **kwargs):
        print("Foo --new--")
        return object.__new__(cls)

    def __call__(self, *args, **kwargs):
        print("Foo --call--", args, kwargs)


# 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo("Alex")

类的生成 调用 顺序依次是 __new__ --> __call__ --> __init__

反射

class Dog(object):

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


def eat(self, food):
    print("%s is eating...%s" % (self.name, food))


d = Dog("Jack")

if hasattr(d, "eat"):
    getattr(d, "eat")("meat")
else:
    setattr(d, "eat", eat)
    getattr(d, "eat")(d, "meat")

delattr(d, "eat")
d.eat(d, "meat")

Python7 面向对象