python 面向对象 (成员 + 组合(嵌套))

 

1. 类的成员

面向对象中类的成员有三大类 :  2 + 3 + 1      每个都包括公有私有(名称前加双下划线) 

变量 : 

  - 实例变量 (字段)          公有和私有

  - 类变量 (静态字段)       公有和私有

方法 :

  - 实例方法

  - 静态方法 @staticmethod

  - 类方法  @classmethod

属性 :

  - 属性    @property   只有self参数   有返回值

1.1 变量

class Foo:
    # 类变量(静态字段)
    country = "中国"

    def __init__(self, name):
        # 实例变量(字段)
        self.name = name

    def func(self):
        pass

obj1 = Foo('季红')
obj2 = Foo('王晓东')
print(obj1.name)   #实例变量用对象访问

print(Foo.country)  # 类变量用类直接访问

总结:

  实例变量(字段) : 在构造函数中声明, 使用对象访问,即 : obj.name

  类变量(静态字段) : 在类中(非方法中)声明, 建议使用类访问,即 : Foo.country 

           对象也可以访问,但只会改变自己的值,不会使类中值改变,不建议使用.

1. 什么时候使用类变量?

当所有对象中有共同的字段时且要改都改,要删都删时,可以将 实例变量(字段) 提取到 类变量(静态字段) 中.

2. 易错点 , 对于类变量访问:

# 对象访问类变量时, 只改变obj中的,不会改变类中类变量country的值
# 类访问类变量时, 会改变类变量country的值,创建的对象都会改变
obj1 = Foo('季红')
obj2 = Foo('王晓东')

# 练习1
# obj1.name = 'alex'  # 只改变obj1中的内容
# print(obj1.name) # alex
# print(obj2.name) # 王晓东

# 练习2
# obj1.country = '美国'  # 对象访问类变量时, 只改变obj中的,不会改变类中类变量country的值
# print(obj1.country) # 美国
# print(obj2.country) # 中国

# 练习3
# Foo.country = '美国'   # 类访问类变量时, 会改变类变量country的值,创建的对象都会改变
# print(obj1.country) # 美国
# print(obj2.country) # 美国
小练习

3. 字段成员修饰符  (公有和私有)

公有和私有的区别 :

公有变量, 在类的内部和外部都可以进行访问

私有变量, 只能在类的内部进行访问, 但是可以通过类中方法进行间接访问, 在变量前面加双下划线. 

# ######################## 公有实例变量(字段) ########################

class Foo:

    def __init__(self,name):
        self.name = name   # 公有实例变量
        self.age = 123

    def func(self):
        print(self.name)  # 内部访问

obj = Foo('张凯雄')
print(obj.name)  # 外部访问
print(obj.age)
obj.func()  
公有 实例变量(字段)
# ######################## 私有实例变量(私有字段) ########################
class Foo:

    def __init__(self,name):
        self.__name = name   # 私有实例变量(私有字段) (前面加双下划线)
        self.age = 123   # 公有实例变量

    def func(self):
        print(self.__name)   # 内部访问 私有 实例变量

obj = Foo('张凯雄')
print(obj.age)  
#obj.__name # 无法访问
obj.func()  # 找一个内部人:func, 让func帮助你执行内部私有 __name
            # 通过方法间接访问私有变量
私有 实例变量(私有字段)
# ######################## 公有类变量(静态字段) ########################
class Foo:
    country = "中国"  # 公有 类变量

    def __init__(self):
        pass

    def func(self):
        print(self.country)  # 内部调用
        print(Foo.country)   # 内部调用 也可直接使用类名


obj = Foo()
obj.func()   # 通过方法间接调用

print(obj.country)  #  对象直接访问类变量  不建议使用,
print(Foo.country)  # 外部直接调用  推荐使用
公有 类变量(静态字段)
# ######################## 私有类变量(私有静态字段) ########################
class Foo:
    __country = "中国"   # 私有 类变量

    def __init__(self):
        pass

    def func(self):
        # 内部调用
        print(self.__country)
        print(Foo.__country)  # 推荐

# 外部无法调用私有类变量
# print(Foo.country)

obj = Foo()
obj.func()
私有 类变量(私有静态字段)

其中: 在类的继承中,私有变量(默认指的是变量前加的双下划线)也不能被子类(儿子)访问.

注意: 

  “单下划线” 开始的成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量;
  “双下划线” 开始的是私有成员,意思是只有类对象自己能访问,连子类对象也不能访问到这个数据。

思考: 如何验证儿子都不知道私有字段的存在.

# 无法访问:
class Base(object):
    __secret = "受贿"

class Foo(Base):

    def func(self):
        print(self.__secret)  # 无法访问 父类中的私有变量
        print(Foo.__secret)

obj = Foo()
obj.func()

# 可以访问:
class Base(object):
    __secret = "受贿"

    def zt(self):
        print(Base.__secret) 

class Foo(Base):

    def func(self):
        print(self.__secret)
        print(Foo.__secret)

obj = Foo()
obj.zt()  # 通过父类中的方法间接访问父类中的私有变量
子类中无法直接访问父类的私有变量

1.2 方法

class Foo(object):
    def __init__(self, name):  # 构造方法也属于实例方法
        self.name = name

    # 实例方法,self是对象, 使用对象中封装的值
    def func(self):
        print(self.name)

    # 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
    @staticmethod
    def display(a1,a2):
        return a1 + a2

    # 类方法,cls是类, 传递的是该类
    @classmethod
    def show(cls,x1,x2):
        print(cls,x1,x2)

# 执行实例方法, 通过对象来调用访问
obj = Foo('..')
obj.func()

# 执行静态方法, 建议使用类访问
Foo.display(1, 3)

# 执行类方法, 直接使用类访问
Foo.show(1,8)

总结 :  实例方法/ 静态方法/ 类方法 有什么区别?      (编写  执行  使用场景)

实例方法 :

  1) 编写, 有 self 参数, self 传递的是对象

  2) 执行, 创建对象,通过对象调用   对象.方法名()

  3) 使用场景, 使用到对象封装在__init__中值变量时

静态方法 : 

  1) 编写, 方法上方有 @staticmethod , 没有self 参数, 其他参数可有可无

  2) 执行, 建议使用类直接调用,  类.方法名()    对象.方法名()

  3) 使用场景, 无需使用对象中已封装的值

类方法 : 

  1) 编写, 方法上方有 @classmethod , 至少有一个cls参数, 传递的是当前类, python会自动进行传递

  2) 执行, 直接只用类来调用,   类.方法名()  # 默认会将当前类传到参数中

  3) 使用场景, 如果在方法中会使用到当前类,那么就可以使用类方法.

1. 方法成员修饰符  (公有和私有)

私有,同样是在方法名前面加双下划线,且私有的方法,只能在内部访问.

# ########################## 私有的实例方法 #######################
class Foo(object):

    def __init__(self):
        pass

    def __display(self,arg):   # 双下划线 私有方法
        print('私有方法',arg)

    def func(self):
        self.__display(123)

obj = Foo()
# obj.__display(123) # 无法直接访问私有方法
obj.func()  # 通过方法 间接访问私有方法
私有 实例方法
# ########################## 私有的静态方法 #######################
class Foo(object):

    def __init__(self):
        pass

    @staticmethod
    def __display(arg):
        print('私有静态 方法',arg)

    def func(self):
        Foo.__display(123)

    @staticmethod
    def get_display():
        Foo.__display(888)

# Foo.__display(123) 报错

obj = Foo()
obj.func()

Foo.get_display()
私有 静态方法

 类方法 也存在私有,这里不再举例.

1.3 属性

属性是通过方法改造的,调用类似于变量,调用时方法名不加括号,  如  obj.func

class Foo(object):
    def __init__(self):
        pass

    @property    # 属性,只有self一个参数, 有返回值
    def start(self):
        return 1

    @property
    def end(self):
        return 10

obj = Foo()
print(obj.start)  # 使用对象调用,调用时方法名不加括号.
print(obj.end)

总结 :

属性 :

  1) 编写, 方法上方有 @property,  只有self一个参数,  有返回值

  2) 执行, 调用时方法名无需加括号,   对象.方法名

  3) 使用场景, 对于简单的方法,当无需传参且有返回值时,可以使用属性

 

练习题 : 将多条数据进行分页展示.

# 以前写法
data_list = []

for i in range(1, 901):
    data_list.append('alex-%s' % i)

while True:
    # 1. 要查看的页面
    page = int(input('请输入要查看的页码:'))

    # 2. 每页显示 10 条
    per_page_num = 10

    start = (page-1) * per_page_num
    end = page * per_page_num

    page_data_list = data_list[start:end]
    for item in page_data_list:
        print(item)


# 面向对象的写法
class Pagenation(object):
    """
    处理分页相关的代码
    """

    def __init__(self,data_list,page,per_page_num=10):
        """
        初始化
        :param data_list: 所有的数据
        :param page: 当前要查看的页面
        :param per_page_num: 每页默认要显示的数据行数
        """
        self.data_list = data_list
        self.page = page
        self.per_page_num = per_page_num

    @property
    def start(self):
        """
        计算索引的起始位置
        :return:
        """
        return (self.page-1) * self.per_page_num

    @property
    def end(self):
        """
        计算索引的结束位置
        :return:
        """
        return self.page * self.per_page_num

    def show(self):
        """
        展示当前页的数据
        :return:
        """
        result = self.data_list[self.start:self.end]
        for row in result:
            print(row)


data_list = []
for i in range(1, 901):
    data_list.append('alex-%s' % i)

while True:
    # 1. 要查看的页面
    page = int(input('请输入要查看的页码:'))
    obj = Pagenation(data_list,page)  
    obj.show()
分页展示数据

2. 组合(嵌套)

 面向对象, 类与类之间有组合,大白话就是所谓的嵌套. 

"""
创建三个学校且三个学校的设施内容等都是一致.
"""

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

    def speech(self):
        print('讲课')

obj1 = School('北京校区', '美丽富饶的沙河')
obj2 = School('上海校区', '浦东新区')
obj3 = School('深圳校区', '南山区')

class Teacher(object):
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.__salary = salary
        self.school = None

t1 = Teacher('李杰', 19, 188888)
t2 = Teacher('艳涛', 18, 60)
t3 = Teacher('女神',16, 900000)

# ############## 老师分配校区
t1.school = obj1
t2.school = obj1
t3.school = obj2

# ##############################
# 查看t1老师,所在的校区名称/地址
print(t1.school.name)
print(t1.school.address)
print(t1.name)
print(t1.age)
t1.school.speech()

 组合, 类与类之间的嵌套,  选课小练习 (学校/课程/班级) 之间的嵌套 :

https://www.cnblogs.com/cyycyhcbw/articles/9554630.html

组合小补充 : 

  确定对象中封装了什么?

   self 到底是谁?

练习题 (10个)  重点

https://www.cnblogs.com/cyycyhcbw/articles/9555263.html

 

posted @ 2018-08-28 10:45  葡萄想柠檬  Views(1184)  Comments(0)    收藏  举报
目录代码