类与对象


内容概要

  • 面向过程编程
  • 面向对象编程
  • 类的定义和对象的产生
  • 类属性和对象属性的查找顺序
  • 绑定方法
  • 非绑定方法
  • 隐藏属性
  • property装饰器

内容详细

一、面向过程编程

面向过程编程编程思想:
解决问题的流程,好比设计一条流水线,按顺序执行代码,先做什么,再做什么,后做什么

例子:
把大象放冰箱中需要几步?
'''
1. 把冰箱门打开
2. 把大象放进去
3. 关上冰箱门
'''

优点:把复杂的问题流程化,简单化
缺点:不易修改,牵一发而动全身,不易维护,扩展性差

二、面向对象编程

1、面向对象编程(oop)是一种程序设计思想。oop把对象作为程序的基本单元,一个对象包含数据和操作数据的函数

2、在python中,所有数据类型都被视为对象,也可以自定义对象。自定义对象数据类型就是面向对象中类的概念

面向对象核心是对象二字
什么是对象?
1. 程序中:
函数:盛放数据的容器
对象:盛放数据和函数的容器
2. 现实生活中:
一切皆对象
对象:特征与节能的结合体
eg:

优点:扩展性强,可维护性强
缺点:编程复杂度高
def choose_course(stu_dict, course):
    stu_dict['course'].append(course)
    print('%s选课成功 %s' % (stu_dict['name'], stu_dict['course']))


stu1 = {
    'name': 'jason',
    'age': 18,
    'gender': 'male',
    'course': [],
    'choose_course': choose_course
}

stu2 = {
    'name': 'ly',
    'age': 18,
    'gender': 'male',
    'course': [],
    'choose_course': choose_course
}

stu1['choose_course'](stu1, 'python')
stu2['choose_course'](stu2, 'python')

三、类的定义

对象:是属性(数据)与功能的结合体
类:一系列对象相似的特征与相似的技能的结合体

强调:站在不同的分类,划分的分类不一定一样

1. 程序中:
    必须先定义类,然后调用类产生对象
2. 现实生活中:
    先有对象,在有人类
	
	
'''
python的语法定义类:
    1. 定义函数
        def 函数名():
            pass

    2. 定义类:
        class 类名():
            pass
'''

# 定义类发生的三件事:
	1. 立即执行类体代码
	2. 产生一个类的名称空间,把类体里面执行的名字都扔到名称空间中(大字典)
	3. 把类的名称空间绑定给__dict__, 类名.__dict__

# 约定俗成:类名一般首字母大写

class Student():
    # 定义一个属性
    school = 'SH'

    # 定义一个技能(函数)
    def choose_course(stu_dict, course):
        stu_dict['course'].append(course)
        print('%s选课成功 %s' % (stu_dict['name'], stu_dict['course']))

    print('>>>>>>')

# 查看类的名称空间
print(Student.__dict__)


# 产生对象
# 调用类产生对象,默认产生的就是一个空对象{}

stu1 = Student()
stu2 = Student()
stu3 = Student()

print(stu1.__dict__)  # {}
print(stu2.__dict__)  # {}
print(stu3.__dict__)  # {}

四、给对象定制自己独有的属性

class Student():
    # 学生对象都有个共同的学校属性
    school = 'oldboy'
    
    # 其中也有一个魔法 __new__,会自动调用把对象赋值给一个空字典
    
	# 初始化语法
    # self 就是对象名,在实例化对象时会自动传入对象名
    def __init__(self, name, age, gender, course=None)
    	if course = None:
            course = []
    	self.name = name
		self.age = age
		self.gender = gender
         self.course = course
    # return None # 在这个方法中不能有返回值,
	
    # 类中的方法
    def choose_course(self, courses):
		self.course.append(courses)

调用类发生了几件事?
'''
1、产生一个空对象
2、产生一个空对象的名称空间
3、让对象指向类的名称空间
4、调用了Student.__dict__方法(空对象,'jacky Liu', 22, 'male'),将空对象和类所需要的值传到类里
5、执行__init__方法为对象增加属性,并将产生的名字放到空对象的名称空间里,得到一个初始化对象(有值的对象)
'''

定义对象,也叫实例化对象
# print(stu1.__dict__) --> {}
stu1 = Student(stu1, 'elijah', 18, 'male')


1、产生一个空对象
2、产生一个空对象的名称空间
3、让对象指向类的名称空间
4、调用了Student.__dict__方法(空对象,'jacky Liu', 22, 'male'),将空对象和类所需要的值传到类里
5、执行__init__方法为对象增加属性,并将产生的名字放到空对象的名称空间里,得到一个初始化对象(有值的对象)

而实际使用中,调用类实例化对象时,会自动将对象名给传入类,所以可以省略第一个参数

stu1 = Student('elijah', 18, 'male')

'''
内部就是字典添加值的方式
stu1.__dict__['name'] = 'elijah'
stu1.__dict__['age'] = 18
stu1.__dict__['gender'] = 'male'
'''
    
print(stu1.__dict__)  --> {'name': 'elijah', 'age': 18, 'gender': 'male'}

五、类和对象的增删改查

# 属性的查找:
# 1. 类属性: 在类中写的属性就称为类属性
# 2. 对象属性:在对象自己的名称空间中的属性就是对象属性

# 类属性的查找
# 1. 查
# print(Student.school)

# 2. 增加
# Student.country = 'China'


# 3. 改
# Student.school = 'BJ'

# 4. 删除
# del Student.school
# print(Student.__dict__)
# {}
stu = Student('ly', 18, 'male')  # 实例化对象, stu就是一个实例
# 对象属性
# 1. 查
# print(stu.name)
# print(stu.age)
# print(stu.gender)

# 2. 增
# stu.aaa = 'aaa'
#
# # 3. 改
# stu.name = 'bbb'

# 4. 删除
# del stu.name
# print(stu.__dict__)


# 类中的方法,类可以调用,对象也可以调用
# 类调用方法
# Student.choose_course(stu, 'python') # 类来调用,函数的参数有几个就要传几个
# print(Student.choose_course)

# 对象调用方法
# 类中的方法,类可以调用,对象也可以调用, 但是,推荐对象来调用,因为对象来调用,会把自己当成第一个参数传递给函数
stu.choose_course('python') # stu.choose_course(stu, 'python')

class Teacher():
    pass
print(isinstance(123, int))
print(isinstance(stu, Teacher))

类和对象属性的查找顺序:

定义类执行后,类会产生一个名称空间

实例化对象,每实例化一个对象都会单独产生一个属于该对象的名称空间
对象对它的属性进行增删改查操作并不能改变类中的属性,只能改对象本身名称空间中的属性

先从对象中找,再到类中找

注意:类中的方法,类可以调用,对象也可以调用, 但是,推荐对象来调用,因为对象来调用,会把自己当成第一个参数传递给函数

六、绑定方法

绑定方法分两类,一种绑定给对象,一种绑定给类

1、绑定给对象

class Student():
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
	
    def tell_info(self):
        print('学生名字:%s 年龄:%s' % (self.name, self.age))

stu1 = Student('elijah', 18)
stu1.tell_info()
'''绑定给对象,对象名会自动被当作第一个参数传入方法函数中'''

2、绑定给类(需要用到装饰器@classmethod)

# 只要有  ip + port(端口)  就可以准确定位到一台电脑的某一个应用程序APP
class Mysql():

    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    @classmethod
    def get_obj(cls):
        obj = cls('127.0.0.1', 3306)  # obj = self.__class__(settings.ip, setting.port)
        return obj


obj = Mysql.get_obj()
print(obj.ip)
print(obj.port)

@classmethod:
    一般调用类中的方法函数,都会自动将对象名当作第一个参数传入函数
	使用类调用方法时,如果加上装饰器@classmethod,可以自动将类名当作第一个参数传入方法中

七、非绑定方法

# 静态方法   @staticmethod
import random
class Mysql():
	
    def __init__(self, ip, port):
    	self.ip = ip
        self.port = port
    
    def get_num(self):
        num = random.randint(1, 9)
        print(num)

'''
像上面这种,get_num方法里的函数体代码根本用不着自动传进来的对象名self
就可以使用静态方法,去掉把对象名自动传入的功能
'''
import random
class Mysql():
	
    def __init__(self, ip, port):
    	self.ip = ip
        self.port = port
    
    @staticmethod
    def get_num():
        num = random.randint(1, 9)
        print(num)

obj = Mysql('127.0.0.1', 3306)
obj.get_num()

八、隐藏属性

1、为何要隐藏属性

在类中有一些属性是不希望能从外面直接进行修改的,把这种属性隐藏起来
如果要修改这类属性,在类中定义一些修改名字的方法,这样可以添加上一些限制条件

隐藏属性对外不对内,在类中可以进行修改(其实在外部也可以修改,但是不建议)


'''
    1. 在类定义阶段,发生了语法上的变形_类名__属性名
    2. 隐藏对外不对内
    3. 只有在类定义阶段发生变形,其他情况都不发生变形了

为什么要隐藏: 类里面的隐藏属性, 类外部可以使用,但是目的不是让类外部使用的,类外部要是想用,在类内部开放接口进行访问
            可以达到对外部数据的严格控制
'''

2、怎么样隐藏属性

语法: __属性名

class Student():
    
    __school = 'oldboy'   # _Student__school = 'oldboy'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 直接查询school无法成功

stu1 = Student('elijah', 18)
print(stu1.school)  # 直接报错

# 使用__school的方法也无法查询成功
print(stu1.__school)

# 得使用_Student__school 的方式查询,因为隐藏属性的实际形式是:_类名__属性名,但不建议在外部查询隐藏属性
print(stu1._Student__school)  # oldboy

print(Student.__dict__)
# # {'__module__': '__main__', '_Student__school': 'oldboy', '__init__': , '__dict__': , '__weakref__': , '__doc__': None}

3、对隐藏属性进行修改

class Student():

    __school = 'oldboy'

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

    def check_school(self):
        print(self.__school)  # print(_Student__school)

    # 在类体内定义一个修改隐藏属性的方法
    def change_school(self, new):
        if not isinstance(new, str):
            print('学校名必须为字符串')
            return
        self.__school = new
        print('修改成功!')


stu1 = Student('elijah', 18)

stu1.check_school()  # oldboy
stu1.change_school('SH_oldboy')  # 修改成功!
stu1.change_school(123)  # 学校名必须为字符串
stu1.check_school()  # SH_oldboy

九、property装饰器

可以让对象在调用方法时不用再添加括号

class Student():
    __name = 'elijah'

    # 定义函数供对象查看类中的隐藏属性

    def check_name(self):
        try:
            print(self.__name)
        except Exception:
            print('没有该属性')

    def change_name(self, new):
        if not isinstance(new, str):
            print('名字必须是字符串')
            return
        self.__name = new
        print('修改成功!')  # 只是在对象自己的独立空间中新创了变量名__name = new

    def del_name(self):
        try:
            del self.__name
            print('删除成功')
        except Exception:
            print('对象不可以删除类中的属性')

stu1 = Student()
stu1.check_name()  # elijah
stu1.change_name('jason')  # 修改成功!只是在对象自己的独立空间中新创了变量名__name = new
stu1.check_name()  # jason,对象自己名称空间中的属性
stu1.del_name()  # 删除成功, 删除了对象独立空间中的属性
stu1.check_name()  # elijah, 对象独立空间中没有__name,从类中拿到的

property的使用

class Student():
    __name = 'elijah'

    # 定义函数供对象查看类中的隐藏属性
    @property
    def name(self):
        print(self.__name)

    # 当对象进行改操作时会自动触发, 而且方法名要与property下的方法名一致
    @name.setter
    def name(self, new):
        if not isinstance(new, str):
            print('名字必须是字符串')
            return
        self.__name = new
        print('修改成功!')

    # 当对象进行删除操作时触发, 而且方法名要与property下的方法名一致
    @name.deleter
    def name(self):
        print('不让删')

stu1 = Student()
stu1.name = 123  # 名字必须是字符串
stu1.name = 'jason'  # 修改成功!
stu1.name  # jason
del stu1.name  # 不让删

了解:

class Student():
    
    __name = 'elijah'
    
    # 定义函数供对象查看类中的隐藏属性
    
    def check_name(self):
        print(self.__name)
	
    
    def change_name(self, new):
        if not isinstance(new, str):
            print('名字必须是字符串')
            return
        self.__name = new
        print('修改成功!')


	def del_name(self):
		del __name
        
	
    name = property(self.check_name, self.change_name, self.del_name) 


stu1 = Student()    
print(stu1.name)