类和对象

一. 类和对象的概念

概念:

面向对象的两个最重要的概念:类和对象

:共性事物的抽象,是对某一类具有共性事物的描述,是概念上的定义

对象:对象是共性事物的一个体现,是这类事物的每个个体,或者说是类的一个实例(instance)

 

总结:

类是对象的模板,对象是类的实例

 

类结构:

里面包含属性与函数

 

数据是对象的状态-->成员变量(属性)

方法是对象的行为-->函数(方法)

二. 语法

#创造类的语法
class Math:
    a = 4                #属性
    b = 5

    def add(self):       #方法
        c = self.a + self.b
        return c

注意:1) 类名一般首字母大写,比如 class User    #class是关键字

     2) 类里面包含属性和方法

   3) 类函数自带self关键字(指向类的实例)。不能少!

   4) 如果类函数里面要调用属性,方法: self.属性名, self.方法名

 

实例:

class Friend:

    height = None
    weight = None
    age = None
    money = None

    #定义功能——会做饭
    def canCook(self, can=True):
        if can == True:
            print("我会做饭。。。")
        else:
            print("我不会做饭。。。")

    def canGetMoney(self):
        print("我会挣钱。。。")

    def setAge(self, age):
        self.age = age
        print("年龄为:", self.age)

    def setHeight(self, height):
        self.height = height
        print("身高为:", self.height)

    def setWeight(self, weight):
        self.weight = weight
        print("体重为:", self.weight)

    def setMoney(self, money):
        self.money = money
        print("资产为:", self.money)

#对象一:身高180,会做饭,做挣钱,有200万的资产,而且才29岁
#实例化:类名()
danqing = Friend()
danqing.setAge(29)
print(danqing.age)
danqing.setMoney(2000000)
print(danqing.money)
danqing.canCook()

print("===========================")
#对象二:身高175,不会做饭,年龄只有25岁,资产5万
dancf = Friend()
#改变对象的属性
dancf.setAge(25)
print(dancf.age)
dancf.setMoney(50000)
print(dancf.money)
dancf.setHeight(175)
print(dancf.height)
dancf.canCook(False)

运行结果:

年龄为: 29
29
资产为: 2000000
2000000
我会做饭。。。
===========================
年龄为: 25
25
资产为: 50000
50000
身高为: 175
175
我不会做饭。。。

三. 初始化函数的调用

初始化函数:def __init__(self, 参数1, 参数2, 参数3)

__init__函数,是两个下划线!经常会写错,写成单下划线,这样会报错!

 

用法:

def __init__(self, a, b):
    self.a = a 
    self.b = b
    self.c =  10

注意:

1) 初始化里面做的是初始化操作,可以带参数也可以不带参数

2) 跟普通函数一样,可以带默认参数

3) 初始化里面可以有赋值好了的属性值

4) 每次创造一个实例,需要传递跟初始化函数参数个数一致的值

5) 每个实例都会自动调用初始化函数

6) 初始化参数的写法要注意,怎么把参数赋值给self.参数名,参数名字不一定要一致,但是赋值要正确

 

实例1:

class Friend:

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


    #定义功能——会做饭
    def canCook(self, can=True):
        if can == True:
            print("我会做饭。。。")
        else:
            print("我不会做饭。。。")

    def canGetMoney(self):
        print("我会挣钱。。。")
xiaozhai
= Friend(26, "", 178) jian = Friend(18, "", 155)

实例2:类也可以没有初始化

class Friend:

    #定义功能——会做饭
    def canCook(self, can=True):
        if can == True:
            print("我会做饭。。。")
        else:
            print("我不会做饭。。。")

    def canGetMoney(self):
        print("我会挣钱。。。")

    def setAge(self, age):
        self.age = age
        print("年龄为:", self.age)

    def setHeight(self, height):
        self.height = height
        print("身高为:", self.height)

    def setWeight(self, weight):
        self.weight = weight
        print("体重为:", self.weight)

    def setMoney(self, money):
        self.money = money
        print("资产为:", self.money)

jian = Friend()
print(jian.age)


运行结果:
Traceback (most recent call last):
  File "D:/python_workshop/python6/revise/类和对象(一).py", line 63, in <module>
    print(jian.age)
AttributeError: 'Friend' object has no attribute 'age'

当直接调用函数后,再打印属性发现有age了,因为函数setAge里生成了一个全局属性age

jian = Friend()
jian.setAge(25)
print(jian.age)


运行结果:
年龄为: 25
25

 

四. 类的继承

 

1. 子类拥有父类的所有属性和行为

2. 子类可以扩展自己的属性和行为

3. 父类的行为不够用,子类要升级和优化,子类可重写父类的方法——多态

4. 子类和父类都有的行为:子类实例优先使用自己的,没有再去用父类的

 

支持多继承

多继承语法:用逗号隔开父类

      class 子类类名(父类1, 父类2):

class Son(Father, Mother):

  def __init__():

    XXXX

实例

class Father:

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

    def eat(self, food):
        print("eat:", food)

    def earnMoney(self, money):
        print("Father earn money:", money)


class Mother:

    def sing(self):
        print("xxxxxx")

    def earnMoney(self, money):
        print("Mother earn money:", money)



#子类继承了两个类的所有属性和方法
class Son(Father, Mother):

    #方法重写,调用该方法时会调用自己的方法
    def eat(self, food):
        print("eat:", food)
        print("我吃的更快,比father更快!!!")

    def dance(self):
        print("xxxxxx")


#子类没有定义的初始化方法,但是父类定义了初始化方法
#则子类实例化时会调用父类的初始化方法
jian = Son(123, "jian", "male", 22)
jian.eat("apple")
#当子类继承了多个类中同样的方法时,子类会优先使用第一个继承类的方法(从左向右)
jian.earnMoney(2000)

#父类实例化
jianBB = Father(111, "li", "male", 56)
jianBB.eat("orange")

运行结果

eat: apple
我吃的更快,比father更快!!!
Father earn money: 2000
eat: orange

 

继承—super用法

在子类的行为中,想调用父类的行为,然后再做额外扩展,可以使用super类

语法:super().行为(参数)

 

class Son(Friend):

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

    super().__init__(age, sex)

    #Friend.__init__(age, sex)  这样也可以 

    self.name = name

 

五. 封装数据

 

实现方式:遵循一定的属性和方法命名规约

不希望这些变量或者方法被外部使用

 

• 任何以单下划线_开头的名字都应该是内部实现

即不希望通过实例名字,变量名/方法名来调用,但是python并不会真的阻止别人访问内部名称,子类也可以继承并重写父类的方法,只是一种约定

 

• 以双下划线__开头的名字,仅类自己可访问

继承——这种方法通过继承是无法被覆盖(重写)的。其实也是可以访问的,只不过换成了_类名__变量名/函数名

 

class MyClass:

    def __init__(self):
        self._private_data1 = "私有方式一"
        self.__private_data1 = "私有方式二"
        pass

    def _pri_func(self):
        print("私有方法一")

    def __pri_func(self):
        print("私有方法二")

    def public_func(self ):
        print("公开方法")
        #类内部调用自己的私有方法一
        self._pri_func()

mc = MyClass()
#仍然可以调用单下划线的属性和方法,只不过作为一种约定,告诉别人请不要这样调用
mc._pri_func()
mc._private_data1
#mc.__private_data2  双下划线的方法无法调用

class SonClass(MyClass):

    #子类继承父类的单下划线私有方法并重写,也是可以的
    def _pri_func(self):
        print("我是子类的私有方法")
    
    
    #子类无法继承父类的双下划线私有方法并重写,运行时报错
    def __pri_func(self):
        print("我是子类的私有方法二")

son = SonClass()
son.__pri_func()

运行结果

Traceback (most recent call last):
  File "D:/python_workshop/python6/revise/私有属性和方法.py", line 37, in <module>
    son.__pri_func()
AttributeError: 'SonClass' object has no attribute '__pri_func'
私有方法一

 

哪种方式好

两种不同的编码约定(单下划线和双下划线)来命名私有属性,问题就来了:到底哪种方式好呢?

大多数而言,你应该让你的非公共名称以单下划线开头,但是,如果你清楚你的代码会涉及到子类,并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案

 

六. @property和@属性名.setter

实例属性,除了访问和修改之后,增加其他逻辑处理,如合法性等

@property      #获取属性值:实例.属性名
def age(self):
    return self._age

@age,setter    #给属性赋值时添加了逻辑处理
def age(self.age):
    if age in range(0, 121 ):
        self._age = age
    else:
        print("输入的年龄不符合要求哦!!")

实例

class People:

    #age是people类的属性名
    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        if age in range(0, 121):
            self._age = age
            print(self._age)
        else:
            print("年龄不符合要求")

p = People()
p.age = 134

运行结果:

年龄不符合要求

 

注意:

不能写成return self.age和self.age = age,原因如下

class People:
    '''
    ==========================================
     @property和@属性名.setter可以用来对属性的值进行一定的逻辑处理。
     但是:
    @property和@属性名.setter的小坑
    ===========================================
    在以下代码中,setter函数表示:当你使用self.age时就会自动的去调用setter
    所以在@age.setter对应的函数中,else操作中,有使用self.age。
    所以,这里的self.age相当于又去调用了setter,即又去调用了自己。就进入了一直在调用自己的状态。
    于是,就会报错:递归次数超限制。
    这个是个坑!!!
    如何解决这个问题呢?如果设置可以爬出这个坑呢??

    @property
    def age(self):
        return self.age
    @age.setter
    def age(self,age):
        if age not in range(0,121):
            print("age is not valid data")
        else:
            self.age = age
    ============================================
    ================以下是爬坑策略====================
    为了不要重复的来调用自己,也就是在@age.setter对应的函数当中,不要使用self.age
    所以用了另外一个变量名:self._age。这样子就不会一直调用。
    ====代码不同之处 一:@property对应的函数中,return self._age =======
    ====代码不同之处 二:@age.setter对应的函数中,else:self._age = age =======

    对外部使用者而言,还是age属性,即可以通过实例名.age来设置值。
    对类内部而言,就是_age属性了。
    以下是代码部分
    '''

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


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

    @age.setter
    def age(self,age):
        if age not in range(0,121):
            print("age is not valid data")
        else:
            self._age = age



p = People("jian","female")
p.age = 100
print(p.age)
p.age = 122
print(p.age)

 

 

 

posted @ 2018-06-29 16:29  cnhkzyy  阅读(323)  评论(0编辑  收藏  举报