面向对象--本章总结

参考:https://blog.csdn.net/a2011480169/article/details/73087097

1、类和对象的概念

1>把一类事物的静态属性和动态可以执行的操作组合在一起所得到的这个概念就是类 
2>类的一个个体就是对象,对象是具体的,实实在在的事物 
3>对象是特征与技能的结合体,其中特征和技能分别对应对象的数据属性和方法属性 
4>对象(实例)本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样 
在类内部定义的属性属于类本身的,由操作系统只分配一块内存空间,大家公用这一块内存空间 
5>创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性:而类中有两种属性:数据属性和函数属性,其中类的数据属性是共享给所有对象的,而类的函数属性是绑定到所有对象的。 
6>创建一个对象(实例)就会创建一个对象(实例)的名称空间,存放对象(实例)的名字,称为对象(实例)的属性 
7>在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常。 
8>类的相关方法:

类的相关方法(定义一个类,也会产生自己的名称空间)
类名.__name__   # 类的名字(字符串)
类名.__doc__    # 类的文档字符串
类名.__base__   # 类的第一个父类(在讲继承时会讲)
类名.__bases__  # 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__   # 类的字典属性、名称空间
类名.__module__ # 类定义所在的模块
类名.__class__  # 实例对应的类(仅新式类中)

9>其余概念:

1.创建出类会产生名称空间,实例化对象也会产生名称空间。

2.用户自己定义的一个类,实际上就是定义了一个类型,类型与类是统一的。

3.用户先是从自己的命名空间找,如果找不大,在从类的命名空间找。
    student1.langage = "1111"
    print(student1.__dict__)  ===>先是从自己的命名空间找
    print(Student.__dict__)   ===>然后在从类的命名空间找

4.通过类来访问,访问的是函数,通过对象来访问,访问的是方法,在类内部定义的方式实际上是绑定到对象的身上来用的。
<function Student.fun at 0x000000000267DAE8>
<bound method Student.fun of <__main__.Student object at 0x0000000002684128>>

<function Student.fun at 0x00000000025CDAE8>
<bound method Student.fun of <__main__.Student object at 0x00000000025D4160>>
<bound method Student.fun of <__main__.Student object at 0x00000000025D4198>>


5.总结:类的数据属性是大家共有的,而且大家的内部地址是一样的,用的就是一个
       类的函数属性是绑定到大家身上的,内部地址不一样,绑定方法指的是绑定到对象身上。
       绑定方法:绑定到谁的身上,就是给谁用的,谁来调用就会自动把自己当做第一个参数传入。
       **定义在类内部的变量,是所有对象共有的,id全一样,
       **定义在类内部的函数,是绑定到所有对象的,是给对象来用的,obj.fun()会把obj本身当做
       一个参数来传递。

6.在类内部定义的函数虽然可以由类来调用,但是并不是为了给类用的,在类内部定义的函数的目的就是为了绑定到对象身上的。


7.在类的内部来说,__init__是类的函数属性,但是对于对象来说,就是绑定方法。


8.命名空间的问题:先从对象的命名空间找,随后在从类的命名空间找,随后在从父类的命名
空间找。
print(student1.x)

9.在定义类的时候,可以想什么先写什么。

2、初始化构造函数\__init_的作用

class Student():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print(self.name,self.age)


if __name__ == '__main__':
    #在构造对象的时候会自动调用初始化构造函数
    student = Student("Alex",100)
    # 输出对象:
    print(student)
    print(student.__dict__)

Alex 100
<__main__.Student object at 0x0000000001DC9080>
{'name': 'Alex', 'age': 100}

3、继承

3.1、继承和方法重写

1、一个类从已有的类那里获得其已有的属性与方法,这种现象叫做类的继承 
2、方法重写指在子类中重新定义父类中已有的方法,这中现象叫做方法的重写
3、若A类继承了B类,则aa对象既是A,又是B,继承反映的是一种谁是谁的关系,什么是什么的关系,只有在谁是谁的情况下,才能用继承解决代码冗余的问题。
4、寻找属性和方法的顺序问题:先从对象自己的命名空间中找,然后在自己的类中,最后在从父类当中去找
5、在python3当中,所有的类都是新式,所有的类都直接或者间接的继承了Object
6、在python中,新建的类可以继承一个或多个父类

class
People: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def tell(self): print("%s-%s-%s"%(self.name,self.age,self.sex)) class Student(People): def __init__(self,name,age,sex,salary): # self.name = name # self.age = age # self.sex = sex People.__init__(self,name,age,sex) self.salary = salary def tell(self): print("%s是最棒的!"%self.name) if __name__ == '__main__': student = Student("alex",20,"man",2000) student.tell() alex是最棒的!

3.2、代码示例2:属性的搜索顺序问题

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.tell()

    def tell(self):
        print("%s---%s"%(self.name,self.age))

class Student(People):
    def tell(self):
        print("呵呵!")

if __name__ == '__main__':
    student = Student("alex",20)

查找顺序--》先到对象自己的名称空间查找--》对象自己的类名称空间中找---》类的父类中名称空间

如果类对象的函数属性进行了封装

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.__tell()

    def __tell(self): 
        print("%s---%s" % (self.name, self.age))

class Student(People):
    def __tell(self):
        print("呵呵!")

if __name__ == '__main__':
    student = Student("alex",20)

输出结果:alex---20

3.3、在子类中调用父类的方法

方式一:指名道姓:即父类名.父类方法()

方式二:super().父类方法()

class Foo:
    def test(self):
        print("from foo")

class Bar(Foo):
    def test(self):
        #Foo.test(self)
        super().test()
        print("bar")

if __name__ == '__main__':
    bb = Bar()
    bb.test()

4、组合

1、一个类的属性可以是一个类对象,通常情况下在一个类里面很少定义一个对象就是它本身,实际意义很少 
2、将另外一个对象作为自己的属性成员(自己的一个属性来自于另外一个对象),这就是组合 
3、组合也可以解决代码冗余的问题,但是组合反应的是一种什么有什么的关系。
class Date:
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day
    def tell(self):
        print("%s--%s--%s"%(self.year,self.month,self.day))

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

class Student(People):
    def __init__(self,name,age,sex,year,month,day):
        People.__init__(self,name,age)
        self.sex = sex
        #下面这一步骤就是组合
        self.birth = Date(year,month,day)

if __name__ == '__main__':
    student = Student("alex",25,"man",2015,12,31)
    print("student的birth成员指向了一个Date对象!")
    print("%s"%student.birth)
    student.birth.tell()

》student的birth成员指向了一个Date对象!
<__main__.Date object at 0x0000000002604358>

2015--12--31

4.1、选课系统也用到组合,为老师添加学生对象,为老师添加班级对象,然后遍历出来

示例:继承与组合

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

class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price
    def tell_info(self):
        print('<%s %s %s>' %(self.name,self.period,self.price))

class Teacher(People):
    def __init__(self,name,age,sex,job_title):
        People.__init__(self,name,age,sex)
        self.job_title=job_title
        self.course=[]
        self.students=[]


class Student(People):
    def __init__(self,name,age,sex):
        People.__init__(self,name,age,sex)
        self.course=[]


egon=Teacher('egon',18,'male','沙河霸道金牌讲师')
s1=Student('牛榴弹',18,'female')

python=Course('python','3mons',3000.0)
linux=Course('python','3mons',3000.0)

#为老师egon和学生s1添加课程
egon.course.append(python)
egon.course.append(linux)
s1.course.append(python)

#为老师egon添加学生s1
egon.students.append(s1)


#使用
for obj in egon.course: # 遍历出为老师添加的课程对象
    obj.tell_info()

5、接口的概念

1、通过接口可以实现不相关类的相同行为,可以起到一个标志的作用. 
2、接口提供了不同的类进行相互协作的平台 
3、在python中根本就没有一个叫做interface的关键字,如果非要去模仿接口的概念,可以借助第三方模块 
4、raise:主动抛出异常,本来没有错,主动抛出错。 
raise TypeError(“类型错误”) 

6、抽象类的概念(只能被继承,不能被实例化,规范子类,降低使用复杂度)

1>Python中抽象方法定义的方式:利用abc模块实现抽象类,在Java当中如果一个方法没有执行体就叫做抽象方法
而在Python中不是以执行体的有无作为标准,而是以一个方法是否有@abc.abstractmethod装饰器作为标准,有则是抽象方法
2>抽象方法通过子类的实现可以变成普通的方法
3>抽象方法不存在所谓重写的问题,却存在着实现的问题
4>含有抽象方法的类一定是抽象类,但是抽象类不一定含有抽象方法,此时也就没有什么意义了
5>抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化
import abc
class File(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
        pass
    #抽象类中可以有普通方法
    def write(self):
        print("11111")

class B(File):
    #如果写pass,也是可以的,此时子类将会覆盖掉父类
    def read(self):
        pass

if __name__ == '__main__':
    bb = B()
    bb.read()
    bb.write()
运行结果:
import abc
class File(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
        pass
    #抽象类中可以有普通方法
    def write(self):
        print("11111")

class B(File):
    #如果写pass,也是可以的,此时子类将会覆盖掉父类
    def read(self):
        pass

if __name__ == '__main__':
    bb = B()
    bb.read()
    bb.write()
允许结果:
1111

7、属性与方法的遍历问题(MRO列表)

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查 2.多个父类会根据它们在列表中的顺序被检查 3.如果对下一个类存在两个合法的选择,选择第一个父类

8、super关键字的使用

1、super关键字产生的原因:在子类当中可以通过使用super关键字来调用父类的中相应的方法,简化代码。 
2、使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表。

9、多态的概念

1所谓多态指的是一个父类的引用既可以指向父类的对象,也可以指向子类的对象,它可以根据当前时刻指向的不同,自动调用不同对象的方法,这就是多态的概念。
(当然,Python中的多态没必要理解的这么复杂,因为Python自带多态的性能)
2、多态性依赖于同一种事物的不同种形态
3、Python是一门弱类型的语言,所谓弱类型语言指的是对参数没有类型限制,而这是我们可以随意传入对象的根本原因

10、封装

1、在面向对象中,所有的类通常情况下很少让外部类直接访问类内部的属性和方法,而是向外部类提供一些按钮,对其内部的成员进行访问,以保证程序的安全性,这就是封装 
2、在python中用双下划线的方式实现隐藏属性,即实现封装
3、访问控制符的用法___包括两种:在类的内部与在类的外部
1>在一个类的内部,所有的成员之间彼此之间都可以进行相互访问,访问控制符__是透明的,失效的 2>在一个类的外部,通过_类名_对象的方式才可以访问到对象中的_成员 综上:内部之间可以直接访问,在类的外部必须换一种语法方式进行访问
4、在python当中如何实现一个隐藏的效果呢?答案:在Python里面没有真正意义上的隐藏,只能 从语法级别去实现这件事。
5、在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:子类名__x,而父类中变形成了:父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的

11、@property的用法

1、property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 
2、将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则(使用的原因)
3、一旦给函数加上一个装饰器@property,调用函数的时候不用加括号就可以直接调用函数了
class Student:
    @property
    def fun(self):
        print("1111111")

if __name__ == '__main__':
    student = Student()
    student.fun 

12、绑定方法与非绑定方法

绑定方法的使用: 
1、在类内部定义的方法,在没有被任何装饰器修饰的情况下,就是为了绑定到对象给对象 
用的,self关键字含有自动传值的过程,不管写不写self关键子。 
2、默认情况下,在类内部定义的方法都是绑定到对象的方法。 
3、绑定方法绑定到谁的身上,谁就作为第一个参数进行传入。 
4、绑定到类的方法给对象使用是没有任何意义的。

非绑定方法: 
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法 
不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已 
只有绑定方法才存在自动传值的说法。

总结: 
绑定到对象的方法,调用的时候会将对象参数自动传入, ===>方法上面什么也不加 
绑定到类的方法,调用的时候会将类作为参数自动传入, ===>方法上面加classmethod 
非绑定方法不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。===>static

使用场景: 
看使用什么调用?类对象or类or什么参数都不需要.

模拟数据库登陆的场景。 
(只有类才有实例化的说法)

创建数据库的时候加上一个id属性,指定是哪一个链接。 
每次数据库实例话的时候都要赋值一个id。
import time
import hashlib

def create_id():
    m = hashlib.md5(str(time.tine()).encode("utf-8"))
    return m.hexdigest()

#time.clock()计算的是cpu真实的时间

猜你喜欢

转载自www.cnblogs.com/foremostxl/p/9641498.html