类与封装

  • 类的简介
  • 对象初始化
  • 构造器方法
  • 封装
  • __(双下划线)与 _ (单下划线)
  • property装饰器
  • 总结
  • 实战


类的简介

  • 类也是一个对象!
  • 类就是一个用来创建对象的对象!
  • 类是type类型的对象,定义类实际上就是定义了一个type类型的对象
    举例
a = int(10) # 创建一个int类的实例
b = str('hello') # 创建一个str类的实例
 print(a, type(a))  # 10 <class 'int'>
print(b, type(b))  # hello <class 'str'>

使用类创建对象的流程
1.创建一个变量
2.在内存中创建一个新对象
3.将对象的id赋值给变量

class MyClass:
    pass  # pass表示资源占位符,在此处可写很多代码。


print(MyClass)  # <class '__main__.MyClass'>表示当前的主对象就是MyClass,就是python的main函数。
mc1 = MyClass()  # 等价于Java的 mc1=new MyClass()
mc2 = MyClass()
print(mc1)  # <__main__.MyClass object at 0x0000022FA0717780>。
print(mc2)  # <__main__.MyClass object at 0x0000021E61197780>。

这样创建不太好,改进方法:

result1 = isinstance(mc1, MyClass)
result2 = isinstance(mc1, str)
print(result1,result2)  # True False
# result1、result2都是MyClass的实例,他们都是一类对象
# isinstance()用来检查一个对象是否是一个类的实例   和Java的this关键字功能一致

print(type(MyClass))  # <class 'type'> 类类型(类类型变量、类类方法、类类属性)
print(id(MyClass))  # 1687389264344

对象初始化

class Person1:
    def say_hello(self):  # 第二步定义方法    self表示至少带一个参数
        """
        self表一个对象、属性、方法
        表一个this关键字的作用,区分当前对象
        可以进行封装继承多态
        """
        print('大家好,我是%s'%self.name)  # 字符串的格式化拼接
    def __init__(self):
        # 操作核心代码(包括封装继承多态)
        pass


p1 = Person1()  # 第三步创建对象
p1.name='zhangtao'  # 赋值
# 调用方法
p1.say_hello()
# 以上赋值称为动态赋值

构造器方法

什么是封装? 就是对当前对象的属性方法隐藏起来,不让其他程序访问!

class Dog():
    # 定义一个方法  带参数的构造器方法/初始化变量属性的方法
    def __init__(self,name):  # 此函数/方法可带无穷个参数
        self.name = name  # 标注name属性为当前对象的属性
# d=Dog()  # 不带参构造器
d = Dog('黑狗')  # 带参的
d.name='黑子'  # 对象.属性()  调用属性的语法
print(d.name)

问题来了!如何隐藏name属性?

  • 通过self.hidden_name = name隐藏,用户只知道hidden_name而不知道name
class Dog1():
    def __init__(self , name):
        self.hidden_name = name

    def say_hello(self):
        print('大家好,我是 %s'%self.hidden_name)


d = Dog1("dahuang")
d.say_hello()

接着获取name呢?

  • 定义get_name方法获取
class Dog2():
    def __init__(self , name):
        self.hidden_name = name

    def say_hello(self):
        print('大家好,我是 %s'%self.hidden_name)

    def get_name(self):
        print('这是真的name的值 %s'%self.hidden_name)


d = Dog2("dahuang")
d.say_hello()
d.get_name()

然后除了最后赋值外还有哪种方法能赋值?

  • 定义set_name方法
class Dog3():
    def __init__(self , name):
        self.hidden_name = name

    def say_hello(self):
        print('大家好,我是 %s'%self.hidden_name)

    def get_name(self):
        print('这是真的name的值 %s'%self.hidden_name)

    def set_name(self, name):
        self.hidden_name = name

    # def __get__(self):
    #     print('这是真的name的值 %s' % self.hidden_name)
    #
    #  def __set_name__(self, name):  # set方法除了self外必须至少再带一个函数
    #     self.hidden_name=name
    #
    # def __set__(self, name):
    #     self.hidden_name = name

    """
    set_name方法可以自定义(set_name)也可以利用底层写法(__set_name__)建议使用自定义的 不用调用底层太麻烦
    """
    """
    get_namet方法只能自定义没有底层方法
    """
d = Dog3('heigou')
d.set_name('hei')
d.say_hello()
d.get_name()

封装

__(双下划线)与 _ (单下划线)

可以为对象的属性使用双下划线开头,_ _xxx
双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问。其实隐藏属性只不过是Python自动为属性改了一个名字。实际上是将名字修改为了,_类名__属性名 比如 _ _name -> Person _name

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

    def get_name(self):
        return self.__name

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

p = Person('孙悟空')

print(p.__name) __开头的属性是隐藏属性,无法通过对象访问
p.__name = '猪八戒'
print(p._Person__name)
p._Person__name = '猪八戒'

print(p.get_name())

使用_ _开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用
一般我们会将一些私有属性(不希望被外部访问的属性)以_开头
使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性

class Person:
    def __init__(self,name):
        self._name = name

    def get_name(self):
        return self._name

    def set_name(self , name):
        self._name = name   

p = Person('孙悟空')

print(p._name)

property装饰器

property装饰器,用来将一个get方法,转换为对象的属性
添加为property装饰器以后,我们就可以像调用属性一样使用get方法
使用property装饰的方法,必须和属性名是一样的

class Person:
    def __init__(self,name,age):
        self._name = name
        self._age = age

    # property装饰器,用来将一个get方法,转换为对象的属性
    # 添加为property装饰器以后,我们就可以像调用属性一样使用get方法
    # 使用property装饰的方法,必须和属性名是一样的
    @property    
    def name(self):
        print('get方法执行了~~~')
        return self._name

    # setter方法的装饰器:@属性名.setter
    @name.setter    
    def name(self , name):
        print('setter方法调用了')
        self._name = name        

    @property
    def age(self):
        return self._age

    @age.setter    
    def age(self , age):
        self._age = age   

p = Person('猪八戒',18)

p.name = '孙悟空'
p.age = 28

print(p.name,p.age)

总结

封装是面向对象的三大特性之一
封装指的是隐藏对象中一些不希望被外部所访问到的属性或方法
如何隐藏一个对象中的属性?

  • 将对象的属性名,修改为一个外部不知道的名字

如何获取(修改)对象中的属性?

  • 需要提供一个getter和setter方法使外部可以访问到属性
  • getter 获取对象中的指定属性(get_属性名)
  • setter 用来设置对象的指定属性(set_属性名)

使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
1.隐藏了属性名,使调用者无法随意的修改对象中的属性
2.增加了getter和setter方法,很好的控制的属性是否是只读的
如果希望属性是只读的,则可以直接去掉setter方法
如果希望属性不能被外部访问,则可以直接去掉getter方法
3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
4.使用getter方法获取属性,使用setter方法设置属性
可以在读取属性和修改属性的同时做一些其他的处理
5.使用getter方法可以表示一些计算的属性

实战

#  定义一个狗
class Dog4():
    def __init__(self , name, age, gender, height):
        self.hidden_name = name
        self.hidden_age = age
        self.hidden_gender = gender
        self.hidden_height = height

    def jiao(self):
        print('大家好,我是 %s'%self.hidden_name)

    def yao(self):
        print('这是真的name的值 %s'%self.hidden_age)

    def run(self):
        print('这是真的name的值 %s' % self.hidden_gender)

矩形面积

#  定义矩形
class Rectangle:
    '''
        表示矩形的类
    '''
    def __init__(self,width,height):
        self.hidden_width = width
        self.hidden_height = height

    def get_width(self):
        return self.hidden_width

    def get_height(self):
        return self.hidden_height

    def set_width(self , width):
        self.hidden_width = width

    def set_height(self , height):
        self.hidden_height = height

    def get_area(self):
        return self.hidden_width * self.hidden_height

r = Rectangle(5,2)
print(r.get_area())
r.set_width(10)
r.set_height(20)

print(r.get_area())