【Python】类与对象、自省、数据验证

原文作者:我辈李想
版权声明:文章原创,转载时请务必加上原文超链接、作者信息和本声明。

文章目录

  • 前言
  • 一、写好一个类
    • 1.类的理解
    • 2.类的输入
    • 2.静态方法和类方法
    • 3.私有变量和私有方法
    • 4.封装、继承、多态
    • 5.Mixin 设计模式
  • 二、类的应用
    • 1.数据验证
    • 2.接口调用
      • 1.oauth2接口调用
      • 2.JWT接口调用


前言


一、写好一个类

1.类的理解

在 Python 中,类是对象的抽象模板,用于定义对象的属性和行为。

声明一个类的通用语法如下:

class ClassName:
  # 类的属性和方法

其中,ClassName 为类的名称,类的属性和方法需要缩进一个层级来声明。

类的属性可以是实例属性和类属性。实例属性是每个对象单独拥有的属性,而类属性是所有对象共享的属性。类属性可以通过 ClassName.attribute 来访问,也可以通过 self.attribute 来访问,但是在类属性和实例属性名字相同时,实例属性具有更高的优先级。

类的方法可以是实例方法、类方法和静态方法。实例方法是针对对象的方法,其第一个参数为 self,表示方法所属的对象。类方法是针对类的方法,其第一个参数为 cls,表示方法所属的类。静态方法是不依赖于类或对象的方法,可以通过 ClassName.method 或者 self.method 调用。

下面是一个简单的示例,演示了如何声明一个类,并定义实例属性、类属性和实例方法:

class Person:
  species = 'human'  # 类属性
  def __init__(self, name, age):
    self.name = name   # 实例属性
    self.age = age

  def say_hi(self):
    print(f"Hi, my name is {self.name}. I'm {self.age} years old.")

# 创建对象并调用实例方法
p1 = Person('Alice', 25)
p1.say_hi()

# 访问类属性
print(Person.species)

这个示例中,Person 类具有 nameage 两个实例属性,以及 species 一个类属性,还有一个 say_hi() 实例方法,用来打印一个问候语。我们创建了一个对象 p1,并通过 p1.say_hi() 调用了实例方法。另外,我们还通过 Person.species 访问了类属性。

2.类的输入

通过构造函数 init() 来给类传值。构造函数是一种特殊的方法,用于初始化类的实例对象。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
p = Person('Alice', 25)
print(p.name)  # 输出 'Alice'
print(p.age)   # 输出 25

2.静态方法和类方法

在 Python 中,类中有三种方法:实例方法、类方法和静态方法。下面对这三种方法进行简单介绍:

  1. 实例方法:定义在类中的普通方法,第一个参数默认是 self,代表实例本身。通过调用实例来调用实例方法。实例方法可以访问实例中的属性和方法。
class MyClass:
    def instance_method(self, x):
        print(f"Instance method called with {x}")
        self.x = x # 修改实例属性

my_object = MyClass()
my_object.instance_method(42) # 调用实例方法
print(my_object.x) # 访问实例属性
  1. 类方法:使用 @classmethod 装饰器定义的方法,第一个参数默认是 cls,代表类本身。通过调用类来调用类方法。类方法可以访问类和类中的属性和方法。
class MyClass:
    class_variable = 100

    @classmethod
    def class_method(cls, x):
        print(f"Class method called with {x}")
        cls.class_variable += 1 # 修改类属性

MyClass.class_method(42) # 调用类方法
print(MyClass.class_variable) # 访问类属性
  1. 静态方法:使用 @staticmethod 装饰器定义的方法,与类和实例无关。通过调用类或实例来调用静态方法。静态方法不能访问类或实例中的属性和方法。
class MyClass:
    @staticmethod
    def static_method(x):
        print(f"Static method called with {x}")

MyClass.static_method(42) # 调用静态方法

需要注意的是,类方法和静态方法可以通过类和实例访问,但实例方法只能通过实例访问。此外,类方法可以用于创建工厂方法,静态方法则可以用于实现辅助函数或通用功能。

3.私有变量和私有方法

在 Python 中,没有真正的私有方法和私有变量,但可以使用以下约定来实现类似的效果:

  1. 用一个下划线 _ 开头的方法或变量表示它是私有的,即不应该在类的外部使用。但这仅仅是个约定,实际上这些方法和变量仍然可以从类的外部访问。

  2. 用两个下划线 __ 开头的方法或变量表示它是私有的,且不能从类的外部访问。这是通过 Python 解释器自动对名称进行重命名以实现的,例如 __foo 会被重命名为 _classname__foo,所以从类的外部访问时会出现 AttributeError 错误。

下面是一个示例,演示如何定义私有方法和私有变量:

class MyClass:
    def __init__(self):
        self.public_variable = "I am public"
        self._private_variable = "I am private"
        self.__secret_variable = "I am a secret"

    def public_method(self):
        print("This is a public method")

    def _private_method(self):
        print("This is a private method")

    def __secret_method(self):
        print("This is a secret method")

my_object = MyClass()
print(my_object.public_variable) # 可以访问公共变量
print(my_object._private_variable) # 可以访问私有变量,但这是不推荐的做法
print(my_object._MyClass__secret_variable) # 可以通过名称重命名访问私有变量
my_object.public_method() # 可以调用公共方法
my_object._private_method() # 可以调用私有方法,但这是不推荐的做法
my_object._MyClass__secret_method() # 可以通过名称重命名调用私有方法,但这是不推荐的做法

这里演示了如何访问公共、私有和秘密变量和方法。请注意,虽然可以访问私有变量和方法,但这是不推荐的做法,因为它破坏了封装性和可维护性。建议仅在必要时使用私有变量和方法,并且遵循约定和最佳实践。

4.封装、继承、多态

在 Python 中,类的封装是指通过设置类的访问权限,以隐藏类的属性和方法,防止外部程序对其进行非法访问和修改。Python 中的访问权限有三种:

  • 公有成员:类中没有加任何修饰符的成员,默认为公有成员,可以在程序中任何地方访问。
  • 私有成员:类中以两个下划线开头的成员,属于私有成员,只能在类内部进行访问,外部程序无法访问。
  • 受保护成员:类中以一个下划线开头的成员,属于受保护成员,只能在类内部和子类中进行访问,外部程序无法访问。

下面是一个简单的示例,展示了如何使用上述访问权限:

class Shape:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.__color = 'white'   # 私有成员
        self._opacity = 1.0      # 受保护成员

    def get_color(self):
        return self.__color     # 获取私有成员

    def set_color(self, color):
        self.__color = color    # 设置私有成员

class Circle(Shape):
    def __init__(self, x, y, r):
        super().__init__(x, y)
        self.r = r

    def get_opacity(self):
        return self._opacity    # 获取受保护成员

c = Circle(0, 0, 10)

print(c.x)           # 输出 0
print(c.get_color()) # 输出 'white'

c.set_color('red')   # 修改私有成员

print(c.get_color()) # 输出 'red'

在上面的代码中,我们定义了一个 Shape 基类和一个 Circle 子类。在 Shape 类中,我们定义了 xy 两个公有成员,__color 一个私有成员,_opacity 一个受保护成员。在 Circle 类中,我们继承了 Shape 类中的属性和方法,并定义了 r 属性。

我们创建了一个 Circle 实例对象 c,并访问其公有成员 xy,输出结果为 0。我们调用 get_color() 方法获取私有成员 __color 的值,并输出结果为 'white'。接着,我们调用 set_color() 方法修改私有成员 __color 的值,并调用 get_color() 方法输出结果为 'red'

需要注意的是,Python 中的私有成员和受保护成员并不是真正的私有和受保护,它们只是在名称前添加了一个特殊的前缀,以此来防止外部程序访问。但是,如果需要,仍然可以通过特殊的方法来访问和修改这些成员。

5.Mixin 设计模式

Mixin 是一种 Python 中的设计模式,可用于将可复用的功能添加到类中,而无需使用继承。Mixin 类是一种轻量级的类,其中定义了一些方法和属性,可以与其他类组合使用。Mixin 类的主要作用是将重复的代码封装在一个类中,并将其添加到多个类中,从而减少代码重复和增加代码的可维护性。

首先,我来举一个例子来解释 Mixin 的使用方式。假设我们有多个类需要实现缓存功能,我们可以使用 Mixin 类来实现:

class CacheMixin:
    cache = {}

    def get_cache(self, key):
        return self.cache.get(key)

    def set_cache(self, key, value):
        self.cache[key] = value

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

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

class CachedUser(User, CacheMixin):
    def __init__(self, name, age):
        super().__init__(name, age)

class CachedCustomer(Customer, CacheMixin):
    def __init__(self, name, address):
        super().__init__(name, address)

在上面的代码中,我们定义了一个 CacheMixin 类,其中包含了缓存相关的方法和属性。然后,我们定义了两个类 UserCustomer。最后,我们定义了两个通过继承 UserCustomer 类和 CacheMixin 类的类 CachedUserCachedCustomer,这两个类中就具有了缓存功能。这种方式使得我们可以在不破坏原有类结构的情况下,轻松地添加缓存功能到类中。

Mixin 类的设计也有一些原则需要遵循:

  1. Mixin 类应该只是一个功能单一的类,只包含一个或多个相关的方法或属性,不应该尝试实现完整的类。

  2. Mixin 类应该使用命名空间来避免与其他类冲突。

  3. Mixin 类可以使用抽象类来强制子类实现指定的方法。

  4. 在多个 Mixin 类中出现名称冲突时,可以使用命名空间来避免这种情况。

总之,Mixin 设计模式是一种非常有用的技术,它可以帮助我们将复用的功能添加到类中,并提高代码的重用性和可维护性。

二、类的应用

1.数据验证

from pydantic import BaseModel, validator

class User(BaseModel):
    name: str
    age: int
    
    @validator('name')
    def name_must_contain_space(cls, v):
        if ' ' not in v:
            raise ValueError('Must contain a space')
        return v
    
    @validator('age')
    def age_must_be_positive(cls, v):
        if v < 0:
            raise ValueError('Age must be positive')
        return v

2.接口调用

1.oauth2接口调用

import json
import requests
import requests.auth
from django.conf import settings

CLIENT_ID = "xxxxx"
CLIENT_SECRET = "xxxxx"
Observe_HOST = 'http://192.168.1.0:8080'
TOKEN_URL = Observe_HOST + '/spacety/oauth2/token'
Mission_Edit_MgcType = Observe_HOST + '/spacety/imagingTasks/updateMgcType'


class ConnectObserve(object):
    """与api通信"""

    def __init__(self):
        self.token = None

    def get_token(self):
        client_auth = requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)
        post_data = {"grant_type": "client_credentials"}
        response = requests.post(TOKEN_URL,
                                 auth=client_auth,
                                 data=post_data)
        token_json = response.json()
        print(token_json)
        return token_json["access_token"]

    # 修改成像任务的mgcType
    def mission_edit_mgcType(self, data):
        if not self.token:
            self.token = self.get_token()
        headers = {"Authorization": "Bearer " + self.token}
        ret = requests.post(url=Mission_Edit_MgcType, data=json.dumps(data), headers=headers)
        return ret.json()

2.JWT接口调用

class CommodityPic(object):
    """同步数据至商城"""

    def __init__(self):
        self.username = settings.COMMODITY.get('username')
        self.password = settings.COMMODITY.get('password')
        self.token = None

    def get_token(self):
        headers = {"Content-Type": "application/json"}
        user_info = {
            "username": self.username,
            "password": self.password
        }
        s1 = requests.post(url=settings.COMMODITY.get('host') + '/rest/v01/user/login/',
                           json=user_info,
                           headers=headers)
        if s1.status_code == requests.codes.ok:
            req_data = s1.json()
            self.token = req_data["token"]
            return req_data["token"]
        return None

    def insert(self, pic_data):
        if not self.token:
            self.token = self.get_token()
        headers = {
            "Content-Type": "application/json",
            "Authorization": "Bearer " + self.token
        }
        try:
            ret = requests.post(url=settings.COMMODITY.get('host') + "/pic_query/admin/picdata/",
                                data=json.dumps(pic_data),
                                headers=headers)
            print('ret.json()', ret.json())
            if ret.status_code == requests.codes.ok:
                if ret.json().get('code') == 0:
                    return True
            return False
        except Exception as e:
            print('同步数据至商城', e)
            return False

你可能感兴趣的:(Python开始入门,python,开发语言)