python随笔(类和对象)

python : 3.6

面向对象

类和对象

类 : 是具有某些相同特征行为个体的抽象(模板)。比如,人就是一个抽象概念
对象:是类的具体实现(实例)。比如,李四就是人的具体实现

属性和行为

属性:名词。定义 该类或实例所包含的数据。 比如名字,年龄等数据
行为:动词。定义实例所具有的行为特征。 比如 吃饭,睡觉,打豆豆等行为

构造函数

对象的创建需要使用构造函数。跟其他面向对象语言一样,分为无参构造函数和有参构造函数。默认情况下就提供了无参构造函数,如果需要在对象创建的时候就初始化对象属性,那么应该使用有参构造函数。

class Student:

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

stu1 = Student('张三')
stu2 = STudent('李四')
print(stu1.name)
print(stu2.name)
----------------------
'张三'
'李四'

和Java语言类似的,构造函数 __init__没有具体返回值(返回None)。根据java来理解的话,其实是有返回值的,它的返回值就是就是该类的实例,不过是隐式返回的,而且不能再构造函数中显示返回,否则抛出异常

类变量和实例变量
class Student():
    name  =  'static_name'             #类变量,写在方法之外
    def __init__(name):
        self.name = name    #实例变量,写在__init__方法中

stu = Student('小明')
print(stu.name)
print(Student.name)
-------------------------------------------------
#结果
'小明'
'static_name'

调用对象属性时步骤:

  • 先查找是否定义了同名实例变量,如果存在则直接返回
  • 查找是否定义了同名类变量,存在直接返回
  • 如果找不到则抛出异常AttributeError: 'xx' object has no attribute 'xx'

实例方法调用某个变量时的步骤:

  • 在方法体内是否存在同名的参数,有则直接返回
  • 抛出异常NameError: name 'xxx' is not defined

总结:

  • 静态变量和实例变量的调用需要有前缀(类名或self)
  • 一般来说类变量应该使用类名调用,因为类变量不属于某个具体的对象
  • 实例方法内调用类变量,ClasName.xxxself.__class__.xxxself.xxx(该方式如果存在同名的实例变量,则会调用的是实例

方法

与Java不同的是,python方法没有重载。可能是因为python是动态语言的缘故。

1.实例方法
def funName(self):  # self只是一种约定俗称,也不是关键字。也可以写this等
    pass

实例方法定义和一般方法相似,但是该方法必须有一个参数self, 用于接收调用当前方法的类实例。
比较神奇的是,在python中可以直接使用类名调用实例方法。但是此时必须传入self参数。

class Person:

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

    def get_info(self):
        print("name:"+self.__name+"\tage:"+str(self.age))

person = Pserson("python小白", 20)
#1. person.get_info()
Person.get_info(person)  #2.0 


----------------------------
name:python小白   age:20

上面代码1 和 代码2 的执行结果一样

2.类方法

类方法和实例方法相似,不同的是方法需要使用@classmethod 修饰。@classmethod是装饰器,有点类似java注解。
类方法可以更简单的访问类变量。

@classmethod 
def classMethod(cls):  #cls用于接收 类
    cls.xxx

python的静态方法,只是用于更简单的访问类变量和类方法。正常情况下,类方法只能访问类变量 和方法(不正常的情况,给类方法添加一个self参数,并传入实例,,这样做感觉没什么意义)

class Person:

    sum = 0

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

    def get_info(self):
        print("name:"+self.__name+"\tage:"+str(self.age))

    @classmethod
    def get_num(cls):
        cls.sum += 1
        print(cls.sum)

person = Person("xx",20)
person.get_num()  # 1.  对象调用类方法
Person.get_num() #2.  直接使用类名调用类方法
--------------------------
1
2

从上面可以看出,虽说是类方法但是对象可以正常调用。不是说接收的参数cls代表类吗???。
通过debug,就知道答案了,类方法即使被对象调用参数cls被传入的还是类本身。

3.静态方法
@staticmethod
def staticMethod():
    pass

静态方法没有什么特别的限制。同样对象和类可以直接调用。同样正常情况下不能访问类变量和实例变量 ,类方法和实例方法


方法总结:

  1. 静态方法没有什么好说的,什么都不能调用。除非传入类或者类实例
  2. 类方法,用于方便调用类变量和其他类方法。类实例也可以直接调用,python如果发现调用者是类实例,并且是类方法,则传入self.__class__。(不要问为什么,我猜的)
  3. 实例方法,使用类名也可以调用,但是必须传入self,因为类是抽象的(模板)可以创建很多对象,所以必须知道是调用者是哪个实例。
私有属性或方法
  • __private_attrs:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs
  • __private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类地外部调用。self.__private_methods。

其实这只是一种声明,python没有提供特别的机制来保证属性和方法的私有。

class Student():

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

    def __get_name(self):
        return self.name


stu = Student('xxx')
print(dir(stu))
print(stu._Student__get_name())
-------------------------------------------------------------
['_Student__get_name', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
'xxx'

从上面可以看到多了一个_Student__get_name。属性或方法私有化以后确实无法直接访问,python对此做了一些处理。但是通过增加前缀_ClassName就可以访问了。

注:
前后都有双下划线在python中有特殊含义的函数或变量,不建议自定义的函数或方法这样写

继承
class DerivedClassName(ParentClassName):
    <statement>

python还支持多继承

class DerivedClassName(ParentClassName1, ParentClassName2...):
    <statement>

定义一个父类

   class Person:    

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

        def get_name(self):
            return seld.__name
  1. 调用父类构造函数:
    隐式调用:可以不写构造函数,此时创建子类实例时,会隐式调用父类的构造函数。此时需要传入和父类构造函数相同数目的参数。否则会抛出异常TypeError: __init__() missing xx个 required positional arguments

        class Student(Person):
              pass
    
        stu = Student("xxx", 10)
        # stu = Student()   会抛出 TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'
    

    显式调用

    1. 如果子类构造函数要显式调用父类的构造函数,可以通过父类名称调用构造函数

      class Student(Person):

      def __init__(self, name, age, addr)
          Person.__init__(self, name , age)
          self.__addr = addr
      

      stu = Student(‘马什么梅’, 20, ‘电影’)
      print(stu.get__name())

    2. 通过super

       class Student(Person):
      
              def __init__(self, name, age):
                   super(Student,self).__init__(name, age)
      

    当然也可以显式不调用父类的构造函数,但是这样做一般就失去了继承的用意。推荐使用第二种,因为第一种代码耦合性高

  2. 调用父类私有属性,或方法

  3. 调用子类与父类重名的属性或方法。
    子类会覆盖父类中同名的方法和变量。如果要调用则需要显式调用。也是使用super关键字

类的专有方法

类的专有方法:

__init__ :  #构造函数,在生成对象时调用
__del__ : #析构函数,释放对象时使用
__repr__ : #打印,转换
__setitem__ : #按照索引赋值
__getitem__: #按照索引获取值
__len__: #获得长度
__cmp__: #比较运算
__call__: #函数调用
__add__:#加运算
__sub__: #减运算
__mul__: #乘运算
__div__: #除运算
__mod__: #求余运算
__pow__: #乘方
枚举

继承 Enum类

from enum inpotr Enum
class (Enum):
from enum import Enum
class Season(Enum):
    SRPING = 1
    SUMMER = 2
    AUGUEST = 3
    WINNER = 4

print(Season.SRPING)  # 跟普通类不同的是,返回并不是变量值
print(Season.SPRING.value)  # 返回具体值
print(Season.SPRING.name)  # 返回标签名
print(type(Season.SRPING)) #实际上是一个定义枚举类的对象
print(Season.SPRING.__dict__)
print(Season(1))   # 根据值获取对应的枚举
-----------------------------
Season.SRPING 
1
<enum 'Season'>
{'_value_': 1, '_name_': 'SPRING', '__objclass__': <enum 'Season'>}
Season.SRPING 

枚举的好处:

  1. 可以防止出现相同的标签(对象?)。TypeError: Attempted to reuse key: 'spring'
  2. 枚举标签不能被修改(常量),强制赋值会报错,AttributeError: Cannot reassign members.
  3. 直接拿到的是标签名,而不是具体的值。这样可以直接根据标签名来识别变量的作用

注意:

  • 枚举 之间不能做 >,< 等比较操做(可以等值比较)
  • 如果一个枚举类,有多个枚举的值相同,则其他的枚举名称为最先出现枚举 的别名

猜你喜欢

转载自blog.csdn.net/yamadeee/article/details/81668050