面向对象基础知识

类的定义与使用

#1、 面向过程与面向对象

# 面向过程:
#     核心过程二字,过程即解解决问题的步骤,先干什么,再干什么
#     基于该思想写程序就好比在设计一条流水线,是一种机械式的思维方式
#     优点:复杂过程流程化,进而简单化
#     缺点:扩展性差

# 面向对象
#     核心是对象二字,对象是特征与技能的结合体
#     基于该思想编写程序就好比在创造一个世界,世界是由一个个对象组成的,是一种"上帝式"的思维方式
#     优点:可扩展性强
#     缺点:变成复杂度高,容易出现过度设计

# 2、类
# 对象是特征与技能的集合体,类是一系列对象相似的特征和技能的集合体
# 在现实世界中:一定是先有的一个个具体存在的对象,后总结出的类
# 在程序中:一定保证先定义类,后产生对象

#类体代码在类的定义阶段就会立刻执行
class Student:
    school = 'oldboy'

    def learn(self):
        print('lenaring learing')

    def choose_course(self):
        print("choose course")

Student.learn('asdf')

对象的定义与使用

# 调用类的过程又称为实例化
# 1、得到一个返回值,即对象,该对象是一个空对象stu1
# 2、Student.__init__(stu1,name,age,sex)
class Student:
    school = 'oldboy'

    def __init__(self,name,age,sex):
        self.Name = name
        self.Age = age
        self.Sex = sex

    def learn(self):
        print('lenaring learing')

    def choose_course(self):
        print("choose course")

stu1 = Student('haha',18,'male')
stu2 = Student('hehe',23,'fmale')
print(stu1.__dict__)
print(stu1.school)
print(stu2.Name)
print(stu2.school)

属性查找与绑定方法


# 在类定义的时候会生成一个类的名称空间,类的名称空间会包括类的数据属性名称和方法属性名称
# 在对象实例化后会生成对象的名称空间,会产生对象的数据属性名称,以及自定义对象的方法属性名称
# 对象的属性查找会现在对象自己的名称空间查找,如果没有会从类的名称空间查找,如果两者名称空间都有查找的属性,优先在对象的名称空间查找。
# 类里的方法是绑定给对象使用的,对象调用类的方法时,对自动将对象作为第一个参数传递给该方法。
class Teacher:
    count=0
    def __init__(self,name,course):
        count = 0
        self.Name = name
        self.Course = course
        Teacher.count+=1

    def teach_course(self):
        print("%s teaching course is %s" %(self.Name,self.Course))

th1 = Teacher('egon','python')
th2 = Teacher('oldboy','linux')
th1.teach_course()
print(th1.teach_course)  #绑定方法
print(th1.count)

类的三大特性

继承

1.什么是继承?
是一种新建类的方式,新建类称为子类,子类会遗传父类的属性,可以减少代码冗余
在python中,子类(派生类)可以继承多个父类(基类,超类)

在python2中,类分为:金典类和新式类
    新式类:继承了object类的类,以及该类的子类
    金典类:没有继承object类的类,以及该类的子类
在python3中,统一都为新式类
'''
class OldboyPeople():
    school="oldboy"

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

    def tell_info(self):
        print("<名字:%s,年龄:%s,性别:%s>" %(self.name,self.age,self.age))

class OldboyStudent(OldboyPeople):

    def learn(self):
        print("%s is learning" %self.name)

    def tell_info(self):
        print("我是学生:",end='')
        print("<名字:%s,年龄:%s,性别:%s>" %(self.name,self.age,self.age))

class OldboyTeacher(OldboyPeople):

    def teach(self):
        print("%s is teaching" %self.name)

stu1=OldboyStudent('huazai',12,'male')  #实例化一个对象
stu1.learn() #调用对象的方法
stu1.tell_info() #
teach1=OldboyTeacher('egon','18','male')
print(teach1.name) #调用对象自己名称空间的属性
teach1.teach() #调用类的名称空间的属性方法
teach1.tell_info()  #调用父类名称空间的方法

#多继承
# 金典类:深度优先
# 新式类:广度优先
#在继承中,属性的查找顺序:对象自己-->类-->父类

继承的实现原理

#多继承
# 金典类:深度优先
# 新式类:广度优先

# 在python2中存在金典类和新式类,在python3中都是新式类
#
class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    def test(self):
        print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__)
#通过上述的例子,分别在python2和python3中测试,可以得出如下的继承顺序:
# 新式类:F-->D-->B-->E-->C-->A
# 金典类:F-->D-->B-->A-->E-->C
#继承的原理就是按照类的mro列表从左到右的顺序来的。
# 三条准则:
# 1.子类会先于父类被检查
# 2.多个父类会根据它们在列表中的顺序被检查
# 3.如果对下一个类存在两个合法的选择,选择第一个父类

super()
# 通过继承的方式查找
在python2中super(当前类名,self)
class Animal:
    shool="oldboy"

class OldboyPeople(Animal):
    # school="oldboy"

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

    def tell_info(self):
        print("<名字:%s,年龄:%s,性别:%s>" %(self.name,self.age,self.age))

class OldboyTeacher(OldboyPeople):

    def __init__(self,name,age,sex,level,salary):
        # OldboyPeople.__init__(self,name,age,sex)  #指定类 的方法调用,不存在严格意义上的继承关系
        # super().__init__(name,age,sex) #通过super方法可以继承父类的相应的属性
        super(OldboyTeacher,self).__init__(name,age,sex) # 在python2中要加上自己类名和self
        self.level = level
        self.salary = salary

    def tell_info(self):
        print("我是老师:",end="")
        # print("<名字:%s,年龄:%s,性别:%s>" %(self.name,self.age,self.age))
        # OldboyPeople.tell_info(self)
        super().tell_info()

teach1=OldboyTeacher("egon",18,"male","10",'1200')
teach1.tell_info()
print(teach1.shool)
print(OldboyTeacher.mro())

组合

#继承的关系是:xx是xx  学生是人
#组合的关系是:xx有xx  学生有出生日期

class OldboyPeople():
    school="oldboy"

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

    def tell_info(self):
        print("<名字:%s,年龄:%s,性别:%s>" %(self.name,self.age,self.age))

class OldboyStudent(OldboyPeople):

    def learn(self):
        print("%s is learning" %self.name)

    def tell_info(self):
        print("我是学生:",end='')
        print("<名字:%s,年龄:%s,性别:%s>" %(self.name,self.age,self.age))

class OldboyTeacher(OldboyPeople):

    def __init__(self,name,age,sex,level,salary):
        OldboyPeople.__init__(self,name,age,sex)
        self.level = level
        self.salary = salary

    def teach(self):
        print("%s is teaching" %self.name)

class Date:

    def __init__(self,year,mon,day):
        self.year=year
        self.mon=mon
        self.day=day

    def tell_birth(self):
        print("生日是《%s-%s-%s》" %(self.year,self.mon,self.day))

class course:

    def __init__(self,name,price,per):
        pass

stu1=OldboyStudent('huazai',20,'male')
stu1.birth=Date(1992,10,5)  #通过为对象添加属性,来实现组合的效果
stu1.birth.tell_birth() #对象可以通过属性方法调用组合类中的方法

多态


1.多态
同一种事物的多种形态

2.多态性
在不考虑对象具体类型的情况下,直接使用对象(对象下的方法)

import abc
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod  #使得继承该类的子类,必须定义与该方法名称相同的方法函数
    def eat(self):
        pass
    @abc.abstractmethod
    def run(self):
        pass

class Pig(Animal):
    def eat(self):   #如果不定义eat 和run方法会报错
        print("pig eating")  

    def run(self):
        print("pig running")

class People(Animal):
    def eat(self):
        print("people eating")

    def run(self):
        print("people runnnig")

def eat(obj):  #定义一个通用的方法 只要传入对象,就可以调用对象的方法,而不必关心是对象是什么
    obj.eat()

obj1 = Pig()
obj2 = People()
eat(obj1) #
eat(obj2)
# pig1=Pig()
# peo1=People()

封装

封装之如何隐藏

封装:
__开头的属性,只是在语法意义上的变形,并不能真的发生变形

这种变形之在类定义阶段发生一次,类定义之后在增加的__开头的属性不会变形

外部不能直接访问,内部可以直接访问

class Foo:
    __N=1  #_Foo__N=1

    def __f1(self):
        print("Foo.f1")

print(Foo.__dict__)
print(Foo._Foo__N)

class Foo:

    def f1(self):
        print("Foo.f1")
        self.__f2()   #self._Foo__f2()

    def __f2(self):   #_Foo__f2()
        print("Foo.f2")

class Bar(Foo):
    def f2(self):
        print("Bar.f2")

class Sub(Bar):
    pass

s = Sub()
print(Sub.__mro__)
s.f1()

封装的真正意义

'''
封装:
数据:控制数据
方法:隔离复杂度
'''
class Student:

    def __init__(self,name,age,sex):
        # self.__name = name
        # self.__age = age
        # self.__sex = sex
        self.set_info(name,age,sex)

    def set_info(self,name,age,sex):   #调用者想要修改属性,必须通过统一的借口调用才能实现
        if type(name) is not str:
            raise ("TypeError: name must be str")
        if type(age) is not int:
            raise ("TypeError: age must be int")
        if type(sex) is not str:
            raise ("TypeError: age must be str")
        self.__name = name
        self.__age = age
        self.__sex = sex

    def tell_info(self):  #调用者想要调用属性  也必须通过统一的调用接口来查询
        print("姓名:<%s>,年龄:<%s>,性别:<%s>" %(self.__name,self.__age,self.__sex))

stu1 = Student("huazai",18,'male')
# stu1.__name = "hehe"
stu1.set_info("hehe",18,'male')
stu1.tell_info()

封装之property

'''
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
将函数属性转换成数据属性来访问
'''
# class People():
#     def __init__(self,name,age,hight,weight):
#         self.name = name
#         self.age = age
#         self.hight = hight
#         self.weight = weight
#
#     @property
#     def bmi(self):
#         return self.weight / (self.hight ** 2)
#
# p1 = People('huazai',12,1.8,80)
# p1.hight=1.75
# # print(p1.bmi())
# print(p1.bmi)

class People:

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

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,obj):
        self.__name = obj

    @name.deleter
    def name(self):
        del self.__name

p1 = People("huazai")
# print(p1.name())
# print(p1.name)
p1.name = "Huazai"
print(p1.name)

反射

# getattr()
# setattr()
# delattr()
# hasattr()
# 通过将字符串反射到对应的方法上
class FtpClient(object):

    def __init__(self,host,port):
        # self.conn = conn
        self.host = host
        self.port = port
        self.conn = "conn"

    def interactive(self):
        while True:
            cmd = input(">>").strip()
            if not cmd:continue
            cmd_l = cmd.split()
            print(cmd_l)
            if hasattr(self,cmd_l[0]):   #判断方法是否存在
                func = getattr(self,cmd_l[0])  #将字符串传换成方法
                func(cmd_l)

    def get(self,cmd_l):
        print("getting... ")

    def put(self):
        print("putting...")

client = FtpClient('127.0.0.1',25)
client.interactive()