Python中的观察者模式及其应用

news2024/10/5 20:53:06

观察者模式是设计模式之一,实现一对多依赖,当主题状态变化时通知所有观察者更新。在Python中,通过自定义接口或内置模块实现观察者模式,可提高程序灵活性和扩展性,尤其适用于状态变化时触发操作的场景,如事件驱动系统、数据同步、发布/订阅系统和气象监测系统等。观察者模式具有松耦合、灵活、可复用的优点,但也需注意其可能带来的开闭原则挑战、效率、循环依赖等问题。

一、引言

观察者模式(Observer Pattern)是设计模式中的一种行为型模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象,当主题对象状态发生改变时,会通知所有观察者对象,使它们能够自动更新自己。这种模式使得目标和观察者之间松散耦合,因为观察者只知道他们需要关注的主题发生了变化,而不必了解变化的具体细节。

在Python编程中,观察者模式可以极大地提高程序的可扩展性和灵活性,尤其适用于那些需要在状态变化时执行特定操作的场景。

二、观察者模式的实现

在Python中,我们可以使用内置的collections.abc模块中的MutableSequence抽象基类,或者自定义一个简单的接口来实现观察者模式。下面是一个基于自定义接口的基本实现:

import abc

# 定义观察者接口
class Observer(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def update(self, subject, message):
        pass

# 定义被观察者(主题)类
class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        self._observers.append(observer)

    def detach(self, observer):
        self._observers.remove(observer)

    def notify(self, message):
        for observer in self._observers:
            observer.update(self, message)

# 实现具体的观察者
class ConcreteObserver(Observer):
    def update(self, subject, message):
        print(f"接收到主题({subject})的通知: {message}")

# 使用示例
if __name__ == "__main__":
    subject = Subject()
    observer1 = ConcreteObserver()
    observer2 = ConcreteObserver()

    subject.attach(observer1)
    subject.attach(observer2)

    subject.notify("状态已更新")

三、观察者模式的应用场景

3.1 事件驱动系统

观察者模式可以很好地应用于事件驱动编程,尤其是在构建GUI应用程序、网络I/O处理和异步任务调度等领域。下面是一个简化的事件驱动编程中的观察者模式实现,模拟了一个简单的事件总线(Event Bus)系统:

import abc
from typing import Callable

class Event(metaclass=abc.ABCMeta):
    pass

class MyCustomEvent(Event):
    def __init__(self, data):
        self.data = data

class EventHandler(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def handle(self, event: Event):
        pass

class EventBus:
    def __init__(self):
        self._handlers = {}

    def register_handler(self, event_type: type, handler: EventHandler):
        if event_type not in self._handlers:
            self._handlers[event_type] = []
        self._handlers[event_type].append(handler)

    def unregister_handler(self, event_type: type, handler: EventHandler):
        if event_type in self._handlers and handler in self._handlers[event_type]:
            self._handlers[event_type].remove(handler)

    def publish(self, event: Event):
        event_type = type(event)
        if event_type in self._handlers:
            for handler in self._handlers[event_type]:
                handler.handle(event)

# 具体的事件处理器实现
class CustomEventHandler(EventHandler):
    def handle(self, event: Event):
        if isinstance(event, MyCustomEvent):
            print(f"处理自定义事件,数据为: {event.data}")

# 应用场景
if __name__ == "__main__":
    bus = EventBus()
    custom_handler = CustomEventHandler()

    # 注册事件处理器
    bus.register_handler(MyCustomEvent, custom_handler)

    # 发布事件
    custom_event = MyCustomEvent("Hello, World!")
    bus.publish(custom_event)

在这个例子中,EventBus作为一个中心组件,负责管理和分发事件。EventHandler是观察者接口,任何想要处理事件的类都需要实现这个接口。当有事件发布到事件总线上时,事件总线会查找对应类型的事件处理器,并调用它们的handle方法来处理事件。

MyCustomEvent是具体的事件类型,而CustomEventHandler则是针对该事件类型的具体处理器。在主程序中,我们创建了一个事件总线实例,注册了一个自定义事件处理器,然后发布了一个自定义事件。发布事件后,自定义事件处理器接收到事件并进行了处理。这就是观察者模式在事件驱动编程中的基本应用。

3.2 数据同步

在数据同步的场景中,观察者模式可以用来实现在数据源发生更改时,自动更新所有依赖于该数据的实体。以下是一个简单的Python实现,模拟了一个数据库表数据变化时通知多个订阅者进行数据同步的例子:

import abc

# 定义观察者接口
class DataSyncObserver(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def sync_data(self, updated_data):
        pass

# 定义被观察者(数据源)
class DataSource:
    def __init__(self):
        self._observers = []
    
    def attach(self, observer: DataSyncObserver):
        self._observers.append(observer)
    
    def detach(self, observer: DataSyncObserver):
        self._observers.remove(observer)
    
    def update_data(self, new_data):
        self._data = new_data
        self.notify_observers()
    
    def notify_observers(self):
        for observer in self._observers:
            observer.sync_data(self._data)

# 实现具体的数据同步观察者
class CacheUpdater(DataSyncObserver):
    def __init__(self, cache):
        self.cache = cache
    
    def sync_data(self, updated_data):
        print(f"CacheUpdater: Updating cache with new data: {updated_data}")
        self.cache.update(updated_data)

class FrontendUpdater(DataSyncObserver):
    def __init__(self, frontend):
        self.frontend = frontend
    
    def sync_data(self, updated_data):
        print(f"FrontendUpdater: Updating frontend with new data: {updated_data}")
        self.frontend.render(updated_data)

# 应用场景
if __name__ == "__main__":
    source = DataSource()

    cache_updater = CacheUpdater(Cache())
    frontend_updater = FrontendUpdater(Frontend())

    source.attach(cache_updater)
    source.attach(frontend_updater)

    # 数据源数据发生更改
    source.update_data({"key": "value"})

# 这里假设有Cache和Frontend类,分别代表缓存和前端展示组件
# 并且它们都有update方法用于更新数据

在这个例子中,DataSource是被观察者,它维护了一个观察者列表,并在数据更新时通过notify_observers方法通知所有观察者。CacheUpdaterFrontendUpdater都是数据同步观察者,它们实现了sync_data方法,当接收到数据源的更新通知时,会同步更新自身的缓存或前端显示数据。当数据源的update_data方法被调用时,所有已注册的观察者都会收到更新通知并执行同步操作。

3.3 发布/订阅系统

在发布/订阅(Publish/Subscribe)系统中,观察者模式是非常适合的,因为它可以很好地处理一对多的关系,即一个主题(发布者)可以有多个订阅者(观察者)。发布者发布消息时,所有订阅该主题的观察者都会收到通知。以下是一个基于Python实现的简化的发布/订阅系统示例:

import abc

# 定义消息接口
class Message(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get_message(self) -> str:
        pass

# 定义订阅者接口
class Subscriber(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def receive_message(self, message: Message):
        pass

# 定义发布/订阅系统的核心类
class PubSubSystem:
    def __init__(self):
        self._subscribers = {}

    def subscribe(self, topic: str, subscriber: Subscriber):
        if topic not in self._subscribers:
            self._subscribers[topic] = []
        self._subscribers[topic].append(subscriber)

    def unsubscribe(self, topic: str, subscriber: Subscriber):
        if topic in self._subscribers and subscriber in self._subscribers[topic]:
            self._subscribers[topic].remove(subscriber)

    def publish(self, message: Message):
        topic = message.get_topic()
        if topic in self._subscribers:
            for subscriber in self._subscribers[topic]:
                subscriber.receive_message(message)

# 实现具体的消息类和订阅者类
class NewsMessage(Message):
    def __init__(self, title, content):
        self.title = title
        self.content = content

    def get_topic(self) -> str:
        return "news"

    def get_message(self) -> str:
        return f"Title: {self.title}, Content: {self.content}"

class NewsSubscriber(Subscriber):
    def receive_message(self, message: Message):
        if isinstance(message, NewsMessage):
            print(f"NewsSubscriber received news: {message.get_message()}")

# 应用场景
if __name__ == "__main__":
    pubsub = PubSubSystem()

    subscriber = NewsSubscriber()
    pubsub.subscribe("news", subscriber)

    news_message = NewsMessage("Breaking News!", "Important event occurred.")
    pubsub.publish(news_message)

在这个示例中,PubSubSystem扮演了发布者的角色,它维护了一个字典,键是主题名,值是订阅该主题的观察者列表。当有新的消息发布时,系统会查找订阅了该主题的所有订阅者,并调用它们的receive_message方法。NewsMessage是一个具体的消息类,NewsSubscriber是订阅者类,它定义了如何处理特定类型的消息。在主程序中,我们创建了一个发布/订阅系统实例,订阅了“news”主题,并发布了一个新闻消息。发布后,订阅了该主题的订阅者将会接收到并处理这条消息。

3.4 气象监测系统

在气象监测系统中,我们可以利用观察者模式来实现气象数据变化时自动通知订阅者(如气象分析软件、预警系统等)。下面是一个简化的Python实现:

import abc

# 定义气象数据接口
class WeatherData(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get_temperature(self) -> float:
        pass

    @abc.abstractmethod
    def get_humidity(self) -> float:
        pass

    @abc.abstractmethod
    def get_pressure(self) -> float:
        pass

# 定义观察者接口
class WeatherObserver(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def update(self, weather_data: WeatherData):
        pass

# 定义气象监测站(被观察者)
class WeatherStation:
    def __init__(self):
        self._observers = []
        self._weather_data = WeatherDataImpl()  # 假定WeatherDataImpl是实现了WeatherData接口的具体气象数据类

    def register_observer(self, observer: WeatherObserver):
        self._observers.append(observer)

    def remove_observer(self, observer: WeatherObserver):
        self._observers.remove(observer)

    def measurements_changed(self):
        for observer in self._observers:
            observer.update(self._weather_data)

# 实现具体的观察者类
class WeatherAlertSystem(WeatherObserver):
    def update(self, weather_data: WeatherData):
        temp = weather_data.get_temperature()
        humidity = weather_data.get_humidity()
        pressure = weather_data.get_pressure()

        # 模拟检查天气状况并采取行动
        if temp > 35 or pressure < 950:
            print("Weather Alert System: Sending alerts due to extreme conditions!")

# 应用场景
if __name__ == "__main__":
    station = WeatherStation()
    alert_system = WeatherAlertSystem()

    # 注册观察者
    station.register_observer(alert_system)

    # 模拟气象数据变化
    # (通常这部分由传感器或其他实时数据源提供,这里仅做演示)
    fake_update = WeatherDataImpl(temp=38, humidity=80, pressure=940)
    station._weather_data = fake_update  # 更改气象数据

    # 通知所有观察者数据已改变
    station.measurements_changed()

在这个例子中,WeatherStation是被观察者,它维护一个观察者列表,并在气象数据变化时调用measurements_changed方法通知所有观察者。WeatherObserver是观察者接口,具体观察者(如WeatherAlertSystem)实现了该接口以处理气象数据变化的事件。当气象监测站接收到新的气象数据时,所有已注册的观察者都会收到通知并执行相应的更新操作。

四、优缺点及适用情况

4.1 优缺点

  • 优点:
  1. 松耦合:观察者模式降低了主题(被观察者)和观察者之间的耦合度,它们只需要知道对方的接口即可,无需了解对方的具体实现细节。
  2. 灵活性:观察者可以在运行时动态添加或删除,增强了系统的灵活性和可扩展性。
  3. 复用性:一个被观察者可以同时拥有任意数量的观察者,方便了系统的模块化设计和重用。
  4. 广播通知:当被观察者状态改变时,能够一次性通知所有相关的观察者,简化了状态传播的过程。
  • 缺点:
  1. 开闭原则的挑战:为了支持新的观察者,有时需要修改被观察者的代码以添加新的通知方法。
  2. 效率问题:当观察者数量庞大且频繁更新时,通知所有观察者的操作可能成为性能瓶颈。
  3. 循环依赖:如果没有正确设计,可能会导致观察者和被观察者之间的循环依赖问题。
  4. 控制复杂度:随着系统规模增大,管理观察者列表和通知过程可能变得复杂,特别是在有多种类型的观察者和通知策略的情况下。
  5. 通知顺序无法保证:观察者模式并不保证通知的顺序,如果需要有序的通知,可能需要结合其他设计模式或机制来实现。

此外,Python标准库也提供了threading模块的EventConditionQueue等机制,用于线程间通信和事件通知,可以根据具体应用场景考虑是否采用这些内建工具替代传统的观察者模式。

4.2 实战中的注意事项

在实际应用观察者模式时,有几个关键点需要注意:

  • 避免循环依赖:确保不会出现观察者依赖于被观察者,又被被观察者依赖的情况,否则可能导致无限循环通知。

  • 处理好资源管理:尤其是当观察者数量巨大或者生命周期不确定时,要妥善处理资源释放,防止内存泄露等问题。

  • 控制通知频率:如果观察者数量众多并且频繁更新,可能会影响性能。可通过批处理、节流或截流等手段控制通知的频率和时机。

  • 明确边界和责任:清晰定义被观察者和观察者的职责范围,避免二者职责混淆,造成设计上的混乱。

五、高级特性和优化

在实际项目中,观察者模式可以通过一些优化手段来增强其表现力和实用性。例如:

5.1 异步通知

在高并发或多线程环境中,被观察者发出通知时,可能需要使用异步方式来避免阻塞。Python的asyncio库可以帮助我们将通知过程转换为非阻塞式任务,观察者则通过异步回调函数处理更新。

import asyncio

class AsyncStockPrice(Subject):
   async def notify_observers_async(self, price):
       tasks = [observer.update(self, price) for observer in self._observers]
       await asyncio.gather(*tasks)

class AsyncPriceSubscriber(Observer):
   async def update(self, subject, price):
       print(f"异步股票价格更新:{subject.symbol}当前价格为{price:.2f}")

# 异步示例
loop = asyncio.get_event_loop()
async_stock = AsyncStockPrice('AAPL')
async_subscriber1 = AsyncPriceSubscriber()
async_subscriber2 = AsyncPriceSubscriber()

async_stock.attach(async_subscriber1)
async_stock.attach(async_subscriber2)

loop.run_until_complete(async_stock.notify_observers_async(147.45))

5.2 弱引用

在长期运行的应用中,为了避免内存泄漏,可以使用弱引用(weakref)来持有观察者引用。这样,当没有其他强引用指向观察者时,系统会在适当的时候自动回收资源。

import weakref

class WeakReferenceObserverPattern:
   def __init__(self):
       self._observers = weakref.WeakSet()

   def attach(self, observer):
       self._observers.add(observer)

   def detach(self, observer):
       try:
           self._observers.remove(observer)
       except KeyError:
           pass

   def notify_observers(self, *args, **kwargs):
       observers_copy = list(self._observers)
       for observer in observers_copy:
           observer.update(self, *args, **kwargs)

5.3 过滤器和中介者

在大型系统中,为了更精细地控制事件传递和处理流程,观察者模式可能结合过滤器模式或者中介者模式使用,以控制哪些观察者应该接收到什么样的更新。过滤器可以根据预设条件筛选出需要接收更新的观察者,而中介者则可以集中处理和转发事件,从而简化对象之间的交互。下面是一个简单示例,展示了如何在观察者模式中加入过滤器和中介者概念:

from abc import ABC, abstractmethod
from collections import defaultdict

class Filter(metaclass=ABC):
    @abstractmethod
    def should_notify(self, observer, subject, change):
        pass

class Mediator:
    def __init__(self):
        self._observers = defaultdict(list)
        self._filters = []

    def register_observer(self, observer, filter_=None):
        if filter_ is not None:
            self._filters.append(filter_)
        self._observers[observer].append(filter_)

    def unregister_observer(self, observer):
        del self._observers[observer]

    def notify_observers(self, subject, change):
        for observer, filter_ in self._observers.items():
            if filter_ is None or all(filter_.should_notify(observer, subject, change) for filter_ in filter_):
                observer.update(subject, change)

class Subject:
    def __init__(self):
        self._mediator = Mediator()

    def attach(self, observer, filter_=None):
        self._mediator.register_observer(observer, filter_)

    def detach(self, observer):
        self._mediator.unregister_observer(observer)

    def notify(self, change):
        self._mediator.notify_observers(self, change)

class ConcreteFilter(Filter):
    def should_notify(self, observer, subject, change):
        # 根据change内容决定是否通知观察者,这里只是一个简单的示例
        if change['type'] == 'important':
            return True
        return False

class Observer(ABC):
    @abstractmethod
    def update(self, subject, change):
        pass

class ConcreteObserver(Observer):
    def update(self, subject, change):
        print(f"Observer: Received update: {change}")

# 示例使用
subject = Subject()
observer1 = ConcreteObserver()
filter1 = ConcreteFilter()

subject.attach(observer1, filter1)
subject.notify({'type': 'normal', 'data': 'Normal update'})
subject.notify({'type': 'important', 'data': 'Important update'})

# 输出:
# Observer: Received update: {'type': 'important', 'data': 'Important update'}

在这个示例中,Mediator类替代了原有的直接通知观察者的方式,负责管理观察者列表和过滤器。ConcreteFilter是一个具体的过滤器实现,它定义了何时应该通知观察者。Subject类通过Mediator来注册和通知观察者,并且在通知时通过过滤器进行筛选。这样,只有满足过滤条件的观察者才会接收到通知。

六、总结

在Python中实现观察者模式可以帮助我们更好地组织复杂的业务逻辑,尤其是在处理涉及状态变更和多方响应的情况时。通过合理的应用和适当的优化,可以在多种场景下有效地降低模块之间的耦合度,实现系统内部的良好通信和协调工作。然而,在使用过程中也需要警惕过度使用导致的“扇出”问题(即一个对象的变化可能引发大量其他对象的反应),以及由此带来的性能影响和调试难度的增加。因此,在实际项目中需根据实际情况适度使用观察者模式,并结合其他设计模式来优化整体架构。
在这里插入图片描述


关注gzh不灵兔,Python学习不迷路,关注后后台私信,可进wx交流群,进群暗号【人生苦短】~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1638202.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaSE——算法(2/2):查找算法-二分查找(前言、详细图解、代码部分)

目录 前言 详细图解 代码部分 前言 查找算法中&#xff0c;首要讨论的是基本查找&#xff0c;也就是顺序查找&#xff0c;在数据量特别大的时候&#xff0c;基本查找这种从前往后挨个找的形式&#xff0c;性能是很差的&#xff01; 所以为了提高一些性能&#xff0c;产生了…

MyBatis中的#{} 和 ${}

目录 #{} 和 ${} 预编译 SQL 和 即时 SQL SQL注入 ${}的使用 #{} 和 ${}的使用 MyBatis参数赋值有两种方式&#xff0c;在上一篇文章中&#xff0c;一直使用 #{} 进行赋值&#xff0c;接下来&#xff0c;我们来使用 ${} 进行赋值&#xff0c;并观察 #{} 和 ${} 的区别 使用…

操作系统:线程

目录 前言&#xff1a; 1.线程 1.1.初识线程 1.2.“轻量化”进程 1.3.线程与进程 2.线程控制 2.1.pthread原生线程库 2.2.线程控制的接口 2.2.1.线程创建 2.2.线程退出|线程等待|线程分离|线程取消 2.3.pthread库的原理 2.4.语言和pthread库的关系 2.5.线程局部…

【leetcode】快慢指针相关题目总结

141. 环形链表 判断链表是否有环&#xff1a;如果链表中存在环&#xff0c;则在链表上不断前进的指针会一直在环里绕圈子&#xff0c;且不能知道链表是否有环。使用快慢指针&#xff0c;当链表中存在环时&#xff0c;两个指针最终会在环中相遇。 /*** Definition for singly-…

L2TP连接尝试失败,因为安全层在初始化与远程计算机的协商时遇到一个处理错误。

一、首先这个问题&#xff0c;有一定概率出现&#xff08;已确认&#xff09; 1.使用后未将其断开或者频繁连接断开&#xff0c;导致注册表出现异常。&#xff08;目前推断是这样的&#xff09; 2.系统网卡驱动问题&#xff0c;需要进行网络重置&#xff0c;卸载网卡驱动后重新…

自动化机器学习——网格搜索法:寻找最佳超参数组合

自动化机器学习——网格搜索法&#xff1a;寻找最佳超参数组合 在机器学习中&#xff0c;选择合适的超参数是模型调优的关键步骤之一。然而&#xff0c;由于超参数的组合空间通常非常庞大&#xff0c;手动调整超参数往往是一项耗时且困难的任务。为了解决这个问题&#xff0c;…

基于Springboot的社区医疗服务系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区医疗服务系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

Unity LineRenderer 入门

概述&#xff1a; 如果你在你项目中需要一些渲染线条的效果&#xff0c;在3D场景中&#xff0c;渲染一个线条出来&#xff08;比如路线图&#xff0c;激光射线&#xff0c;标记点&#xff09;等效果&#xff0c;那这部分的学习一定不要错过喔。 Line Renderer&#xff08;线条…

ECHARTS学习

坐标轴 option {xAxis: {type: category,data: [A, B, C]},yAxis: {type: value},series: [{data: [120, 200, 150],type: line}] }; 1、坐标轴的默认类型type是数值型&#xff0c;而xAxis指定了类目型的data&#xff0c;所以Echarts也能识别出这是类目型的坐标轴&#xff0c;…

第八篇:隔离即力量:Python虚拟环境的终极指南

隔离即力量&#xff1a;Python虚拟环境的终极指南 1 引言 在编程的多元宇宙中&#xff0c;Python语言犹如一颗闪耀的星辰&#xff0c;其魅力不仅仅在于简洁的语法&#xff0c;更在于其庞大而繁荣的生态系统。然而&#xff0c;随着应用的增长和复杂性的提升&#xff0c;开发者们…

手搓带头双向循环链表(C语言)

目录 List.h List.c ListTest.c 测试示例 带头双向循环链表优劣分析 List.h #pragma once#include <stdio.h> #include <stdlib.h> #include <assert.h>typedef int LTDataType;typedef struct ListNode {struct ListNode* prev;struct ListNode* next…

上位机图像处理和嵌入式模块部署(树莓派4b读写json数据)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们说过&#xff0c;ini文件是用来进行配置的&#xff0c;数据库是用来进行数据存储的。那json是用来做什么的呢&#xff0c;json一般是用来做…

[HNOI2003]激光炸弹

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二维前缀和板题。 注意从&#xff08;1,1&#xff09;开始存即可&#xff0c;所以每次输入x,y之后&#xff0c;要x,y。 因为m的范围最大为…

使用Neo4j和Langchain创建知识图谱

使用Neo4j和Langchain创建知识图谱 知识图谱是组织和整合信息的强大工具。通过使用实体作为节点和关系作为边缘&#xff0c;它们提供了一种系统的知识表示方法。这种有条理的表示有利于简化查询、分析和推理&#xff0c;使知识图在搜索引擎、推荐系统、自然语言处理和人工智能…

32.基础乐理-相对音感与绝对音感

相对音感的概念&#xff1a; 就是先给你一个音&#xff0c;告诉你这个音是X&#xff0c;然后再给一个Y音&#xff0c;你就能根据 X 音判断出这个 Y 音是什么&#xff0c;原理是在于你掌握的是 X 与 Y 之间相对距离的感觉&#xff0c;比如图1&#xff0c;弹两个键 先弹 小字一组…

Ubuntu GUI使用Root用户登录指南

Ubuntu GUI使用Root用户登录指南 一、前言 默认情况下&#xff0c;Ubuntu 禁用了 root 账户&#xff0c;我们必须使用 sudo 命令来执行任何需要 root 权限的任务&#xff0c;比如像这样删除一个系统配置文件&#xff08;操作危险&#xff0c;请勿尝试&#xff09;&#xff1a;…

python可视化学习笔记折线图问题-起始点问题

问题描述&#xff1a; 起始点的位置不对 from pyecharts.charts import Line import pyecharts.options as opts # 示例数据 x_data [1,2,3,4,5] y_data [1, 2, 3, 4, 5] # 创建 Line 图表 line Line() line.add_xaxis(x_data) line.add_yaxis("test", y_data) li…

安装“STM32F4 Discovery Board Programming with Embedded Coder”MATLAB获取硬件支持包失败

安装“STM32F4 Discovery Board Programming with Embedded Coder”MATLAB获取硬件支持包失败 -完美解决方法 显示请续订您的软件维护服务&#xff0c;解决办法 根据知乎的文章 MATLAB获取硬件支持包失败&#xff0c;显示请续订您的软件维护服务&#xff0c;解决办法&#xff…

《QT实用小工具·五十》动态增删数据与平滑缩放移动的折线图

1、概述 源码放在文章末尾 该项目实现了带动画、带交互的折线图&#xff0c;包含如下特点&#xff1a; 动态增删数值 自适应显示坐标轴数值 鼠标悬浮显示十字对准线 鼠标靠近点自动贴附 支持直线与平滑曲线效果 自定义点的显示类型与大小 自适应点的数值显示位置 根据指定锚点…

程序包的创建

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 前面很多范例中都用到的 dbms output.put_line 实际上就是一个典型的程序包应用&#xff0c; 其中 dbms output是程序包的名称&#xff0c;put_line 是该程序包中定义的一个…