18.面向对象编程、类与对象、属性查找、隐藏属性

  • 引子

  • 面向对象编程

  • 类与对象

  • 属性查找

  • 隐藏属性

  • 面向对象编程

    核心是对象二字,对象就是一个用来盛放数据与功能的容器,也可以说是把数据与功能整合到一起的产物

    基于该思想编写程序就是在创造一个个的容器

    优点:扩展性强()
    缺点:编程的复杂度提升
    应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
    面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性
# 一个学生的数据信息:name,age,gender
name = 'geng'
age = 18
gender= 'male'


# tell_info 功能
def tell_info():
    print('my name is %s my age is %s my gender is %s' %(name,age,gender))

tell_info()
# my name is geng my age is 18 my gender is male

# 此时若想执行查看个人信息的功能,需要同时拿来两样东西,一类是功能,另外一类则是数据,然后才能执行,非常麻烦
    
       
# 把数据和功能整合放到一个容器中,在增加多个学生信息    
def tell_info(student):
    print('my name is %s my age is %s my gender is %s' %(student["name"],student["age"],student["gender"]))
    
student = {
    "school":"上海校区",      
    "name":"geng",
    "age":18,
    "gender":"male",
    "tell_info":tell_info
}

student["tell_info"](student)


student1 = {
    "school":"上海校区",
    "name":"yang",
    "age":19,
    "gender":"female",
    "tell_info":tell_info
}

student1["tell_info"](student1)


# 可以看出两个容器有相同的地方,只有是同一类才能总结出相似的东西

  • 类与对象

    类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体

    那么问题来了,先有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看

    在现实世界中:先有对象,再有类

    在程序中:务必保证先定义类,后产生对象

    这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类

    不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象

  • 定义类发生的事

    1、会立刻运行类体代码
    2、将运行过程中产生的名字都丢到类的名称空间中
class Student:  # 类的命名应该使用“驼峰体”
    school = "上海校区"  # 数据

    def tell_info(student): # 功能
        print('my name is %s my age is %s my gender is %s' % (student["name"], student["age"], student["gender"]))

# 类体最常见的是变量的定义和函数的定义,但其实类体可以包含任意Python代码,类体的代码在类定义阶段就会执行,
# 因而会产生新的名称空间用来存放类中定义的名字,可以打印Student.__dict__来查看类这个容器内盛放的东西

# print(Student.__dict__)
  • 调用类发生的事情

    1、创造一个对象的字典,用来存放对象独有的数据
    2、将对象与类建立好关联
class Student:
    school = "上海校区"  # 共有数据

obj1 = Student()  # 每实例化一次Student类就得到一个学生对象
obj2 = Student()
 #该方法会在对象产生之后自动执行,专门为对象进行初始化操作,可以有任意代码,但一定不能返回非None的值
def init(self,x,y,z):
    self.name = x
    self.age = y
    self.gender = z

init(obj1,"geng",18,"male")  # 重新实例学生
init(obj2,"yang",19,"female")

# 至此,我们造出了两个对象与一个类,对象存放各自独有的数据,类中存放对象们共有的内容

print(obj1.__dict__)
# {'name': 'geng', 'age': 18, 'gender': 'male'}

print(obj2.__dict__)
# {'name': 'yang', 'age': 19, 'gender': 'female'}

print(Student.__dict__)
# {'__module__': '__main__', 'school': '上海校区', '__dict__': <attribute '__di
  • 调用类的过程

    1、先创造一个空对象
    2、自动触发类内的__init__函数的运行,将空对象当做第一个参数自动传入
    3、返回一个初始化好的对象给obj1
class Student:
    school = "上海校区"
    #            空对象, "egon", 18, "male"
    def __init__(self, x, y, z):
        self.name = x  # 空对象.name = "egon"
        self.age = y   # 空对象.age = 18
        self.gender = z  # 空对象.gender = "male"

    def tell_info(student):
        print('my name is %s my age is %s my gender is %s' % (student["name"], student["age"], student["gender"]))


obj1 = Student("egon",18,"male")  # Student.__init__(空对象,"egon",18,"male")
obj2 = Student("tom",19,"female")


print(obj1.__dict__)
print(obj2.__dict__)
print(Student.__dict__)


  • 属性查找

    对象的名称空间里只存放着对象独有的属性,而对象们相似的属性是存放于类中的。对象在访问属性时,会优先从对象本身的__dict__中查找,未找到,则去类的__dict__中查找
class Student:
    school = "上海校区"
    #            空对象, "egon", 18, "male"
    def __init__(self, x, y, z):
        self.name = x  # 空对象.name = "egon"
        self.age = y   # 空对象.age = 18
        self.gender = z  # 空对象.gender = "male"

    def tell_info(self):
        print('my name is %s my age is %s my gender is %s' % (self.name, self.age, self.gender))


    def func(self):
        pass

obj1 = Student("egon",18,"male")
obj2 = Student("tom",19,"female")

# 对象.属性的查找顺序: 先从对象的字典里找,再从类的字典里找

print(obj1.name)  # obj1.__dict__["name"]
print(obj1.school)
print(obj1.tell_info)

obj1.school = "XXX"
print(obj1.school)  # xxx 说明优先从对象字典里找


# 类.属性:从类自己的字典里找(类中属性之所以可以自己调,其实是因为类的属性是为对象准备的)
print(Student.school)
print(Student.__init__)
print(Student.tell_info)

# 类中的数据属性是直接共享给所有对象用的,指向相同的内存地址
Student.school = 'XXXXX'
obj1.school = 'YYYYY'
print(Student.school,id(Student.school)) # 上海校区 1994529963920
print(obj1.school,id(Student.school))    # 上海校区 1994529963920
print(obj2.school,id(Student.school))    # 上海校区 1994529963920


# 类中的函数类可以可用,如果类来调用就是一个普通函数,该怎么传参就怎么传
# 但其实类中的函数是给对象用,对象来调用就是一个绑定方法,绑定方法的特点是会将调用当做第一个参数自动传入
print(Student.tell_info)
Student.tell_info(obj1)
Student.tell_info(obj2)

print(obj1.tell_info)
obj1.tell_info()
obj2.tell_info()

Student.func()
obj1.func()
  • 隐藏属性

    在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
# 类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:
class People:
# 类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的 ↓
    __country = "China"        # 变形为 _People__country = "china"

    def __init__(self,name,age):
        self.__name = name  # self._People__name = name
        self.__age = age    # self._people__age = age

    def __func(self):  # _People__func
        print('xx')

    def tell_name(self):
        print(self.__name)  # self._People__name

print(People._People__country) # 只有在类内部才可以通过__开头的形式访问到.
print(People._People__func)

print(People.__dict__)
obj1 = People("egon",18)
print(obj1.__dict__)
# __开头的属性的特点:
# 1、并没有真的藏起来,只是变形了
# 2、该变形只在类定义阶段、扫描语法的时候执行,此后__开头的属性都不会变形
obj1.__gender = "male" 
print(obj1.__dict__)   
print(obj1.__gender )
# 3、该隐藏对外不对内
obj1.tell_name()


# 为何要隐藏属性
# 1、隐藏数据属性为了严格控制类外部访问者对属性的操作
class People:

    def __init__(self,name,age):
        self.__name = name  # self._People__name = name
        self.__age = age

    def tell_info(self):
        print("<%s:%s>" %(self.__name,self.__age))

    def set_info(self,name,age):
        if type(name) is not str:
            print("名字必须是字符串")
            return
        if type(age) is not int:
            print("年龄必须是数字")
            return
        self.__name = name
        self.__age = age

obj1 = People("egon",18)  
obj1.tell_info()     # <egon:18>

obj1.set_info("tom",28)  
obj1.tell_info()     # <tom:28>

obj1.set_info(123123123123,28)  # 名字必须是字符串
obj1.tell_info()     # <egon:18>


上一篇:深拷贝 与浅拷贝理解与实现


下一篇:欧几里得算法和扩展的欧几里得算法C++递归实现